GCC Code Coverage Report


Directory: ./
File: client/mysqltest.cc
Date: 2022-11-26 14:12:44
Exec Total Coverage
Lines: 4068 4808 84.6%
Branches: 3792 6793 55.8%

Line Branch Exec Source
1 // Copyright (c) 2000, 2022, Oracle and/or its affiliates.
2 //
3 // This program is free software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License, version 2.0,
5 // as published by the Free Software Foundation.
6 //
7 // This program is also distributed with certain software (including
8 // but not limited to OpenSSL) that is licensed under separate terms,
9 // as designated in a particular file or component or in included license
10 // documentation. The authors of MySQL hereby grant you an additional
11 // permission to link the program and your derivative works with the
12 // separately licensed software that they have included with MySQL.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License, version 2.0, for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22
23 /// @file
24 ///
25 /// mysqltest client - Tool used for executing a .test file.
26 ///
27 /// See @ref PAGE_MYSQL_TEST_RUN "The MySQL Test Framework" for more
28 /// information.
29
30 #include "client/client_query_attributes.h"
31 #include "client/mysqltest/error_names.h"
32 #include "client/mysqltest/expected_errors.h"
33 #include "client/mysqltest/expected_warnings.h"
34 #include "client/mysqltest/logfile.h"
35 #include "client/mysqltest/regular_expressions.h"
36 #include "client/mysqltest/secondary_engine.h"
37 #include "client/mysqltest/utils.h"
38 #include "compression.h"
39
40 #include <algorithm>
41 #include <chrono>
42 #include <cmath> // std::isinf
43 #include <limits>
44 #include <new>
45 #include <sstream>
46 #ifdef _WIN32
47 #include <thread> // std::thread
48 #endif
49
50 #include <assert.h>
51 #if defined MY_MSCRT_DEBUG || defined _WIN32
52 #include <crtdbg.h>
53 #endif
54 #ifdef _WIN32
55 #include <direct.h>
56 #endif
57 #include <errno.h>
58 #include <fcntl.h>
59 #include <limits.h>
60 #include <mysql_async.h>
61 #include <mysql_version.h>
62 #include <mysqld_error.h>
63 #include <signal.h>
64 #include <stdarg.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <sys/types.h>
68 #ifndef _WIN32
69 #include <poll.h>
70 #include <sys/time.h>
71 #include <sys/wait.h>
72 #endif
73 #ifdef _WIN32
74 #include <windows.h>
75 #endif
76
77 #include "caching_sha2_passwordopt-vars.h"
78 #include "client/client_priv.h"
79 #include "m_ctype.h"
80 #include "map_helpers.h"
81 #include "mf_wcomp.h" // wild_compare
82 #include "my_compiler.h"
83 #include "my_config.h"
84 #include "my_dbug.h"
85 #include "my_default.h"
86 #include "my_dir.h"
87 #include "my_inttypes.h"
88 #include "my_macros.h"
89 #include "my_openssl_fips.h"
90 #include "my_pointer_arithmetic.h"
91 #include "my_stacktrace.h"
92 #include "my_systime.h" // my_sleep()
93 #include "my_thread_local.h"
94 #include "prealloced_array.h"
95 #include "print_version.h"
96 #include "sql_common.h"
97 #include "template_utils.h"
98 #include "typelib.h"
99 #include "violite.h"
100 #include "welcome_copyright_notice.h" // ORACLE_WELCOME_COPYRIGHT_NOTICE
101
102 #ifdef _WIN32
103 #define SIGNAL_FMT "exception 0x%x"
104 #else
105 #define SIGNAL_FMT "signal %d"
106 #endif
107
108 #ifdef _WIN32
109 #define setenv(a, b, c) _putenv_s(a, b)
110 #define popen _popen
111 #define pclose _pclose
112 #endif
113
114 #define MAX_VAR_NAME_LENGTH 256
115 #define MAX_COLUMNS 256
116 #define MAX_DELIMITER_LENGTH 16
117 #define DEFAULT_MAX_CONN 1024
118 #define REPLACE_ROUND_MAX 16
119
120 /* Flags controlling send and reap */
121 #define QUERY_SEND_FLAG 1
122 #define QUERY_REAP_FLAG 2
123
124 #define APPEND_TYPE(type) \
125 { \
126 dynstr_append(ds, "-- "); \
127 switch (type) { \
128 case SESSION_TRACK_SYSTEM_VARIABLES: \
129 dynstr_append(ds, "Tracker : SESSION_TRACK_SYSTEM_VARIABLES\n"); \
130 break; \
131 case SESSION_TRACK_SCHEMA: \
132 dynstr_append(ds, "Tracker : SESSION_TRACK_SCHEMA\n"); \
133 break; \
134 case SESSION_TRACK_STATE_CHANGE: \
135 dynstr_append(ds, "Tracker : SESSION_TRACK_STATE_CHANGE\n"); \
136 break; \
137 case SESSION_TRACK_GTIDS: \
138 dynstr_append(ds, "Tracker : SESSION_TRACK_GTIDS\n"); \
139 break; \
140 case SESSION_TRACK_TRANSACTION_CHARACTERISTICS: \
141 dynstr_append( \
142 ds, "Tracker : SESSION_TRACK_TRANSACTION_CHARACTERISTICS\n"); \
143 break; \
144 case SESSION_TRACK_TRANSACTION_STATE: \
145 dynstr_append(ds, "Tracker : SESSION_TRACK_TRANSACTION_STATE\n"); \
146 break; \
147 default: \
148 dynstr_append(ds, "\n"); \
149 } \
150 }
151
152 extern CHARSET_INFO my_charset_utf16le_bin;
153
154 // List of error codes specified with 'error' command.
155 Expected_errors *expected_errors = new Expected_errors();
156
157 // List of warnings disabled with 'disable_warnings' command.
158 Expected_warnings *disabled_warnings = new Expected_warnings();
159
160 // List of warnings enabled with 'enable_warnings' command.
161 Expected_warnings *enabled_warnings = new Expected_warnings();
162
163 enum {
164 OPT_COLORED_DIFF = OPT_MAX_CLIENT_OPTION,
165 OPT_CURSOR_PROTOCOL,
166 OPT_EXPLAIN_PROTOCOL,
167 OPT_HYPERGRAPH,
168 OPT_JSON_EXPLAIN_PROTOCOL,
169 OPT_LOG_DIR,
170 OPT_MARK_PROGRESS,
171 OPT_MAX_CONNECT_RETRIES,
172 OPT_MAX_CONNECTIONS,
173 OPT_NO_SKIP,
174 OPT_OFFLOAD_COUNT_FILE,
175 OPT_PS_PROTOCOL,
176 OPT_RESULT_FORMAT_VERSION,
177 #ifdef _WIN32
178 OPT_SAFEPROCESS_PID,
179 #endif
180 OPT_SP_PROTOCOL,
181 OPT_TAIL_LINES,
182 OPT_TRACE_EXEC,
183 OPT_TRACE_PROTOCOL,
184 OPT_VIEW_PROTOCOL,
185 };
186
187 static int record = 0;
188 static char *opt_db = nullptr, *opt_pass = nullptr;
189 const char *opt_user = nullptr, *opt_host = nullptr, *unix_sock = nullptr,
190 *opt_basedir = "./";
191 const char *excluded_string = nullptr;
192 static char *shared_memory_base_name = nullptr;
193 const char *opt_logdir = "";
194 const char *opt_include = nullptr, *opt_charsets_dir;
195 static int opt_port = 0;
196 static int opt_max_connect_retries;
197 static int opt_result_format_version;
198 static int opt_max_connections = DEFAULT_MAX_CONN;
199 static char *opt_init_command = nullptr;
200 static bool opt_colored_diff = false;
201 static bool opt_compress = false, silent = false, verbose = false,
202 trace_exec = false;
203 static bool debug_info_flag = false, debug_check_flag = false;
204 static bool tty_password = false;
205 static bool opt_mark_progress = false;
206 static bool ps_protocol = false, ps_protocol_enabled = false;
207 static bool sp_protocol = false, sp_protocol_enabled = false;
208 static bool no_skip = false;
209 static bool skip_ignored = false;
210 static bool view_protocol = false, view_protocol_enabled = false;
211 static bool opt_trace_protocol = false, opt_trace_protocol_enabled = false;
212 static bool explain_protocol = false, explain_protocol_enabled = false;
213 static bool json_explain_protocol = false,
214 json_explain_protocol_enabled = false;
215 static bool cursor_protocol = false, cursor_protocol_enabled = false;
216 static bool testcase_disabled = false;
217 static bool display_result_vertically = false, display_result_lower = false,
218 display_metadata = false, display_result_sorted = false,
219 display_session_track_info = false;
220 static bool skip_if_hypergraph = false;
221 static int start_sort_column = 0;
222 static bool disable_query_log = false, disable_result_log = false;
223 static bool disable_connect_log = true;
224 static bool disable_warnings = false;
225 static bool disable_info = true;
226 static bool abort_on_error = true;
227 static bool server_initialized = false;
228 static bool is_windows = false;
229 static MEM_ROOT argv_alloc{PSI_NOT_INSTRUMENTED, 512};
230 static const char *load_default_groups[] = {"mysqltest", "client", nullptr};
231 static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos = line_buffer;
232 static bool can_handle_expired_passwords = true;
233 static bool opt_hypergraph = false;
234
235 /*
236 These variables control the behavior of the asynchronous operations for
237 mysqltest client. If --async-client is specified, use_async_client is true.
238 Each command checks enable_async_client (which can be forced off or
239 disabled for a single command) to decide the mode it uses to run.
240 */
241 static bool use_async_client = false;
242 static bool enable_async_client = false;
243
244 // Secondary engine options
245 static const char *opt_offload_count_file;
246
247 static Secondary_engine *secondary_engine = nullptr;
248
249 static uint opt_zstd_compress_level = default_zstd_compression_level;
250 static char *opt_compress_algorithm = nullptr;
251 static uint opt_test_ssl_fips_mode = 0;
252
253 #ifdef _WIN32
254 static DWORD opt_safe_process_pid;
255 static HANDLE mysqltest_thread;
256 // Event handle for stacktrace request event
257 static HANDLE stacktrace_request_event = NULL;
258 static std::thread wait_for_stacktrace_request_event_thread;
259 #endif
260
261 Logfile log_file;
262 // File to store the progress
263 Logfile progress_file;
264
265 /// Info on properties that can be set with '--disable_X' and
266 /// '--disable_X' commands.
267 struct Property {
268 bool *var; // Actual variable
269 bool set; // Has been set for ONCE command
270 bool old; // If set, thus is the old value
271 bool reverse; // Variable is true if disabled
272 const char *env_name; // Environment variable name
273 };
274
275 static struct Property prop_list[] = {
276 {&abort_on_error, false, true, false, "$ENABLE_ABORT_ON_ERROR"},
277 {&disable_connect_log, false, true, true, "$ENABLE_CONNECT_LOG"},
278 {&disable_info, false, true, true, "$ENABLE_INFO"},
279 {&display_session_track_info, false, true, true,
280 "$ENABLE_STATE_CHANGE_INFO"},
281 {&display_metadata, false, false, false, "$ENABLE_METADATA"},
282 {&ps_protocol_enabled, false, false, false, "$ENABLE_PS_PROTOCOL"},
283 {&disable_query_log, false, false, true, "$ENABLE_QUERY_LOG"},
284 {&disable_result_log, false, false, true, "$ENABLE_RESULT_LOG"},
285 {&disable_warnings, false, false, true, "$ENABLE_WARNINGS"},
286 {&enable_async_client, false, false, false, "$ENABLE_ASYNC_CLIENT"}};
287
288 static bool once_property = false;
289
290 enum enum_prop {
291 P_ABORT = 0,
292 P_CONNECT,
293 P_INFO,
294 P_SESSION_TRACK,
295 P_META,
296 P_PS,
297 P_QUERY,
298 P_RESULT,
299 P_WARN,
300 P_ASYNC,
301 P_MAX
302 };
303
304 static uint start_lineno = 0; /* Start line of current command */
305 static uint my_end_arg = 0;
306
307 /* Number of lines of the result to include in failure report */
308 static uint opt_tail_lines = 0;
309
310 static uint opt_connect_timeout = 0;
311
312 static char delimiter[MAX_DELIMITER_LENGTH] = ";";
313 static size_t delimiter_length = 1;
314
315 static char TMPDIR[FN_REFLEN];
316
317 /* Block stack */
318 enum block_cmd { cmd_none, cmd_if, cmd_while, cmd_assert };
319
320 struct st_block {
321 int line; /* Start line of block */
322 bool ok; /* Should block be executed */
323 enum block_cmd cmd; /* Command owning the block */
324 char delim[MAX_DELIMITER_LENGTH]; /* Delimiter before block */
325 };
326
327 static struct st_block block_stack[32];
328 static struct st_block *cur_block, *block_stack_end;
329
330 /* Open file stack */
331 struct st_test_file {
332 FILE *file;
333 char *file_name;
334 uint lineno; /* Current line in file */
335 };
336
337 static struct st_test_file file_stack[16];
338 static struct st_test_file *cur_file;
339 static struct st_test_file *file_stack_end;
340
341 static const char *default_charset = MYSQL_DEFAULT_CHARSET_NAME;
342 CHARSET_INFO *charset_info =
343 &my_charset_utf8mb4_0900_ai_ci; /* Default charset */
344
345 /*
346 Timer related variables
347 See the timer_output() definition for details
348 */
349 static char *timer_file = nullptr;
350 static ulonglong timer_start;
351 static void timer_output(void);
352 static ulonglong timer_now(void);
353
354 static ulong connection_retry_sleep = 100000; /* Microseconds */
355
356 static char *opt_plugin_dir = nullptr;
357
358 /* To retrieve a filename from a filepath */
359 1 const char *get_filename_from_path(const char *path) {
360 1 const char *fname = nullptr;
361
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (is_windows)
362 fname = strrchr(path, '\\');
363 else
364 1 fname = strrchr(path, '/');
365
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (fname == nullptr)
366 return path;
367 else
368 1 return ++fname;
369 }
370
371 static uint opt_protocol = 0;
372
373 #if defined(_WIN32)
374 static uint opt_protocol_for_default_connection = MYSQL_PROTOCOL_PIPE;
375 #endif
376
377 struct st_command;
378 typedef Prealloced_array<st_command *, 1024> Q_lines;
379 Q_lines *q_lines;
380
381 #include "sslopt-vars.h"
382
383 struct Parser {
384 int read_lines, current_line;
385 } parser;
386
387 struct MasterPos {
388 char file[FN_REFLEN];
389 ulong pos;
390 } master_pos;
391
392 /* if set, all results are concated and compared against this file */
393 const char *result_file_name = nullptr;
394
395 typedef struct {
396 char *name;
397 size_t name_len;
398 char *str_val;
399 size_t str_val_len;
400 int int_val;
401 size_t alloced_len;
402 bool int_dirty; /* do not update string if int is updated until first read */
403 bool is_int;
404 bool alloced;
405 } VAR;
406
407 /*Perl/shell-like variable registers */
408 VAR var_reg[10];
409
410 struct var_free {
411 void operator()(VAR *var) const;
412 };
413
414 collation_unordered_map<std::string, std::unique_ptr<VAR, var_free>> *var_hash;
415
416 struct st_connection {
417 MYSQL mysql;
418 /* Used when creating views and sp, to avoid implicit commit */
419 MYSQL *util_mysql;
420 char *name;
421 size_t name_len;
422 MYSQL_STMT *stmt;
423 /* Set after send to disallow other queries before reap */
424 bool pending;
425 };
426
427 struct st_connection *connections = nullptr;
428 struct st_connection *cur_con = nullptr, *next_con, *connections_end;
429
430 /*
431 List of commands in mysqltest
432 Must match the "command_names" array
433 Add new commands before Q_UNKNOWN!
434 */
435 enum enum_commands {
436 Q_CONNECTION = 1,
437 Q_QUERY,
438 Q_CONNECT,
439 Q_SLEEP,
440 Q_INC,
441 Q_DEC,
442 Q_SOURCE,
443 Q_DISCONNECT,
444 Q_LET,
445 Q_ECHO,
446 Q_EXPR,
447 Q_WHILE,
448 Q_END_BLOCK,
449 Q_SAVE_MASTER_POS,
450 Q_SYNC_WITH_MASTER,
451 Q_SYNC_SLAVE_WITH_MASTER,
452 Q_ERROR,
453 Q_SEND,
454 Q_REAP,
455 Q_DIRTY_CLOSE,
456 Q_REPLACE,
457 Q_REPLACE_COLUMN,
458 Q_PING,
459 Q_EVAL,
460 Q_ENABLE_QUERY_LOG,
461 Q_DISABLE_QUERY_LOG,
462 Q_ENABLE_RESULT_LOG,
463 Q_DISABLE_RESULT_LOG,
464 Q_ENABLE_CONNECT_LOG,
465 Q_DISABLE_CONNECT_LOG,
466 Q_WAIT_FOR_SLAVE_TO_STOP,
467 Q_ENABLE_WARNINGS,
468 Q_DISABLE_WARNINGS,
469 Q_ENABLE_INFO,
470 Q_DISABLE_INFO,
471 Q_ENABLE_SESSION_TRACK_INFO,
472 Q_DISABLE_SESSION_TRACK_INFO,
473 Q_ENABLE_METADATA,
474 Q_DISABLE_METADATA,
475 Q_ENABLE_ASYNC_CLIENT,
476 Q_DISABLE_ASYNC_CLIENT,
477 Q_EXEC,
478 Q_EXECW,
479 Q_EXEC_BACKGROUND,
480 Q_DELIMITER,
481 Q_DISABLE_ABORT_ON_ERROR,
482 Q_ENABLE_ABORT_ON_ERROR,
483 Q_DISPLAY_VERTICAL_RESULTS,
484 Q_DISPLAY_HORIZONTAL_RESULTS,
485 Q_QUERY_VERTICAL,
486 Q_QUERY_HORIZONTAL,
487 Q_SORTED_RESULT,
488 Q_PARTIALLY_SORTED_RESULT,
489 Q_LOWERCASE,
490 Q_SKIP_IF_HYPERGRAPH,
491 Q_START_TIMER,
492 Q_END_TIMER,
493 Q_CHARACTER_SET,
494 Q_DISABLE_PS_PROTOCOL,
495 Q_ENABLE_PS_PROTOCOL,
496 Q_DISABLE_RECONNECT,
497 Q_ENABLE_RECONNECT,
498 Q_IF,
499 Q_DISABLE_TESTCASE,
500 Q_ENABLE_TESTCASE,
501 Q_REPLACE_REGEX,
502 Q_REPLACE_NUMERIC_ROUND,
503 Q_REMOVE_FILE,
504 Q_FILE_EXIST,
505 Q_WRITE_FILE,
506 Q_COPY_FILE,
507 Q_PERL,
508 Q_DIE,
509 Q_ASSERT,
510 Q_EXIT,
511 Q_SKIP,
512 Q_CHMOD_FILE,
513 Q_APPEND_FILE,
514 Q_CAT_FILE,
515 Q_DIFF_FILES,
516 Q_SEND_QUIT,
517 Q_CHANGE_USER,
518 Q_MKDIR,
519 Q_RMDIR,
520 Q_FORCE_RMDIR,
521 Q_FORCE_CPDIR,
522 Q_LIST_FILES,
523 Q_LIST_FILES_WRITE_FILE,
524 Q_LIST_FILES_APPEND_FILE,
525 Q_SEND_SHUTDOWN,
526 Q_SHUTDOWN_SERVER,
527 Q_RESULT_FORMAT_VERSION,
528 Q_MOVE_FILE,
529 Q_REMOVE_FILES_WILDCARD,
530 Q_COPY_FILES_WILDCARD,
531 Q_SEND_EVAL,
532 Q_OUTPUT, /* redirect output to a file */
533 Q_RESET_CONNECTION,
534 Q_QUERY_ATTRIBUTES,
535 Q_UNKNOWN, /* Unknown command. */
536 Q_COMMENT, /* Comments, ignored. */
537 Q_COMMENT_WITH_COMMAND,
538 Q_EMPTY_LINE
539 };
540
541 const char *command_names[] = {
542 "connection", "query", "connect", "sleep", "inc", "dec", "source",
543 "disconnect", "let", "echo", "expr", "while", "end", "save_master_pos",
544 "sync_with_master", "sync_slave_with_master", "error", "send", "reap",
545 "dirty_close", "replace_result", "replace_column", "ping", "eval",
546 /* Enable/disable that the _query_ is logged to result file */
547 "enable_query_log", "disable_query_log",
548 /* Enable/disable that the _result_ from a query is logged to result file */
549 "enable_result_log", "disable_result_log", "enable_connect_log",
550 "disable_connect_log", "wait_for_slave_to_stop", "enable_warnings",
551 "disable_warnings", "enable_info", "disable_info",
552 "enable_session_track_info", "disable_session_track_info",
553 "enable_metadata", "disable_metadata", "enable_async_client",
554 "disable_async_client", "exec", "execw", "exec_in_background", "delimiter",
555 "disable_abort_on_error", "enable_abort_on_error", "vertical_results",
556 "horizontal_results", "query_vertical", "query_horizontal", "sorted_result",
557 "partially_sorted_result", "lowercase_result", "skip_if_hypergraph",
558 "start_timer", "end_timer", "character_set", "disable_ps_protocol",
559 "enable_ps_protocol", "disable_reconnect", "enable_reconnect", "if",
560 "disable_testcase", "enable_testcase", "replace_regex",
561 "replace_numeric_round", "remove_file", "file_exists", "write_file",
562 "copy_file", "perl", "die", "assert",
563
564 /* Don't execute any more commands, compare result */
565 "exit", "skip", "chmod", "append_file", "cat_file", "diff_files",
566 "send_quit", "change_user", "mkdir", "rmdir", "force-rmdir", "force-cpdir",
567 "list_files", "list_files_write_file", "list_files_append_file",
568 "send_shutdown", "shutdown_server", "result_format", "move_file",
569 "remove_files_wildcard", "copy_files_wildcard", "send_eval", "output",
570 "reset_connection", "query_attributes",
571
572 nullptr};
573
574 struct st_command {
575 char *query, *query_buf, *first_argument, *last_argument, *end;
576 DYNAMIC_STRING content;
577 size_t first_word_len, query_len;
578 bool abort_on_error, used_replace;
579 char output_file[FN_REFLEN];
580 enum enum_commands type;
581 // Line number of the command
582 uint lineno;
583 };
584
585 TYPELIB command_typelib = {array_elements(command_names), "", command_names,
586 nullptr};
587
588 DYNAMIC_STRING ds_res;
589 DYNAMIC_STRING ds_result;
590 /* Points to ds_warning in run_query, so it can be freed */
591 DYNAMIC_STRING *ds_warn = nullptr;
592 struct st_command *curr_command = nullptr;
593
594 char builtin_echo[FN_REFLEN];
595
596 /* Stores regex substitutions */
597
598 struct st_replace_regex *glob_replace_regex = nullptr;
599
600 struct REPLACE;
601 REPLACE *glob_replace = nullptr;
602 void replace_strings_append(REPLACE *rep, DYNAMIC_STRING *ds, const char *from,
603 size_t len);
604
605 static void cleanup_and_exit(int exit_code) MY_ATTRIBUTE((noreturn));
606
607 void die(const char *fmt, ...) MY_ATTRIBUTE((format(printf, 1, 2)))
608 MY_ATTRIBUTE((noreturn));
609 void abort_not_supported_test(const char *fmt, ...)
610 MY_ATTRIBUTE((format(printf, 1, 2))) MY_ATTRIBUTE((noreturn));
611 void verbose_msg(const char *fmt, ...) MY_ATTRIBUTE((format(printf, 1, 2)));
612 void log_msg(const char *fmt, ...) MY_ATTRIBUTE((format(printf, 1, 2)));
613 void flush_ds_res();
614
615 VAR *var_from_env(const char *, const char *);
616 VAR *var_init(VAR *v, const char *name, size_t name_len, const char *val,
617 size_t val_len);
618 VAR *var_get(const char *var_name, const char **var_name_end, bool raw,
619 bool ignore_not_existing);
620 void eval_expr(VAR *v, const char *p, const char **p_end, bool open_end = false,
621 bool do_eval = true);
622 bool match_delimiter(int c, const char *delim, size_t length);
623
624 void do_eval(DYNAMIC_STRING *query_eval, const char *query,
625 const char *query_end, bool pass_through_escape_chars);
626 void str_to_file(const char *fname, char *str, size_t size);
627 void str_to_file2(const char *fname, char *str, size_t size, bool append);
628
629 void fix_win_paths(const char *val, size_t len);
630
631 #ifdef _WIN32
632 void free_win_path_patterns();
633 #endif
634
635 /* For replace_column */
636 static char *replace_column[MAX_COLUMNS];
637 static uint max_replace_column = 0;
638 void do_get_replace_column(struct st_command *);
639 void free_replace_column();
640
641 static void do_query_attributes(struct st_command *command);
642
643 /* For replace */
644 void do_get_replace(struct st_command *command);
645 void free_replace();
646
647 /* For replace_regex */
648 void do_get_replace_regex(struct st_command *command);
649 void free_replace_regex();
650
651 /* For replace numeric round */
652 static int glob_replace_numeric_round = -1;
653 void do_get_replace_numeric_round(struct st_command *command);
654 void free_replace_numeric_round();
655 void replace_numeric_round_append(int round, DYNAMIC_STRING *ds,
656 const char *from, size_t len);
657
658 /* Used by sleep */
659 void check_eol_junk_line(const char *eol);
660
661 static void var_set(const char *var_name, const char *var_name_end,
662 const char *var_val, const char *var_val_end);
663
664 12774354 static void free_all_replace() {
665 12774354 free_replace();
666 12774354 free_replace_regex();
667 12774354 free_replace_column();
668 12774354 free_replace_numeric_round();
669 12774354 global_attrs->clear();
670 12774354 }
671
672 /*
673 To run tests via the async API, invoke mysqltest with --async-client.
674 */
675 class AsyncTimer {
676 public:
677 823 explicit AsyncTimer(std::string label)
678 823 : label_(label), time_(std::chrono::system_clock::now()), start_(time_) {}
679
680 823 ~AsyncTimer() {
681 823 auto now = std::chrono::system_clock::now();
682 823 auto delta = now - start_;
683 [[maybe_unused]] ulonglong micros =
684 823 std::chrono::duration_cast<std::chrono::microseconds>(delta).count();
685
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 823 times.
823 DBUG_PRINT("async_timing",
686 ("%s total micros: %llu", label_.c_str(), micros));
687 823 }
688
689 26769410 void check() {
690 26769410 auto now = std::chrono::system_clock::now();
691
1/2
✓ Branch 0 taken 26769410 times.
✗ Branch 1 not taken.
26769410 auto delta = now - time_;
692 26769410 time_ = now;
693 [[maybe_unused]] ulonglong micros =
694
1/2
✓ Branch 0 taken 26769410 times.
✗ Branch 1 not taken.
26769410 std::chrono::duration_cast<std::chrono::microseconds>(delta).count();
695
3/8
✓ Branch 0 taken 26769410 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 26769410 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 26769410 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
26769410 DBUG_PRINT("async_timing", ("%s op micros: %llu", label_.c_str(), micros));
696 26769410 }
697
698 private:
699 std::string label_;
700 std::chrono::system_clock::time_point time_;
701 std::chrono::system_clock::time_point start_;
702 };
703
704 #ifdef _WIN32
705 /*
706 Check if any data is available in the socket to be read or written.
707 */
708 static int socket_event_listen(my_socket fd) {
709 int result;
710 fd_set readfds, writefds, exceptfds;
711
712 FD_ZERO(&readfds);
713 FD_ZERO(&writefds);
714 FD_ZERO(&exceptfds);
715
716 FD_SET(fd, &exceptfds);
717 FD_SET(fd, &readfds);
718 FD_SET(fd, &writefds);
719
720 result = select((int)(fd + 1), &readfds, &writefds, &exceptfds, NULL);
721 if (result < 0) {
722 DWORD error_code = WSAGetLastError();
723 verbose_msg("Cannot determine the status due to error :%lu\n", error_code);
724 }
725 return result;
726 }
727 #else
728 26726873 static int socket_event_listen(my_socket fd) {
729 int result;
730 pollfd pfd;
731 26726873 pfd.fd = fd;
732 /*
733 Listen to both in/out because SSL can perform reads during writes (and
734 vice versa).
735 */
736 26726873 pfd.events = POLLIN | POLLOUT;
737
1/2
✓ Branch 0 taken 26726873 times.
✗ Branch 1 not taken.
26726873 result = poll(&pfd, 1, -1);
738
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26726873 times.
26726873 if (result < 0) {
739 perror("poll");
740 }
741 26726873 return result;
742 }
743 #endif
744
745 /*
746 Below async_mysql_*_wrapper functions are used to measure how much time
747 each nonblocking call spends before completing the operations.
748 i*/
749 206 static MYSQL_ROW async_mysql_fetch_row_wrapper(MYSQL_RES *res) {
750 MYSQL_ROW row;
751 206 MYSQL *mysql = res->handle;
752
2/4
✓ Branch 0 taken 206 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 206 times.
✗ Branch 3 not taken.
412 AsyncTimer t(__func__);
753
2/4
✓ Branch 0 taken 206 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 206 times.
206 while (mysql_fetch_row_nonblocking(res, &row) == NET_ASYNC_NOT_READY) {
754 t.check();
755 int result = socket_event_listen(mysql_get_socket_descriptor(mysql));
756 if (result == -1) return nullptr;
757 }
758 206 return row;
759 206 }
760
761 110 static MYSQL_RES *async_mysql_store_result_wrapper(MYSQL *mysql) {
762 MYSQL_RES *mysql_result;
763
2/4
✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 110 times.
✗ Branch 3 not taken.
220 AsyncTimer t(__func__);
764
3/4
✓ Branch 0 taken 168864 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 168754 times.
✓ Branch 3 taken 110 times.
168864 while (mysql_store_result_nonblocking(mysql, &mysql_result) ==
765 NET_ASYNC_NOT_READY) {
766
1/2
✓ Branch 0 taken 168754 times.
✗ Branch 1 not taken.
168754 t.check();
767
2/4
✓ Branch 0 taken 168754 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 168754 times.
✗ Branch 3 not taken.
168754 int result = socket_event_listen(mysql_get_socket_descriptor(mysql));
768
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 168754 times.
168754 if (result == -1) return nullptr;
769 }
770 110 return mysql_result;
771 110 }
772
773 20 static int async_mysql_real_query_wrapper(MYSQL *mysql, const char *query,
774 ulong length) {
775 net_async_status status;
776
2/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
40 AsyncTimer t(__func__);
777
3/4
✓ Branch 0 taken 5373232 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5373212 times.
✓ Branch 3 taken 20 times.
5373232 while ((status = mysql_real_query_nonblocking(mysql, query, length)) ==
778 NET_ASYNC_NOT_READY) {
779
1/2
✓ Branch 0 taken 5373212 times.
✗ Branch 1 not taken.
5373212 t.check();
780
2/4
✓ Branch 0 taken 5373212 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5373212 times.
✗ Branch 3 not taken.
5373212 int result = socket_event_listen(mysql_get_socket_descriptor(mysql));
781
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5373212 times.
5373212 if (result == -1) return 1;
782 }
783
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (status == NET_ASYNC_ERROR) {
784 return 1;
785 }
786 20 return 0;
787 20 }
788
789 112 static int async_mysql_send_query_wrapper(MYSQL *mysql, const char *query,
790 ulong length) {
791 net_async_status status;
792
2/4
✓ Branch 0 taken 112 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
224 AsyncTimer t(__func__);
793
3/4
✓ Branch 0 taken 211 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 99 times.
✓ Branch 3 taken 112 times.
211 while ((status = mysql_send_query_nonblocking(mysql, query, length)) ==
794 NET_ASYNC_NOT_READY) {
795
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 t.check();
796
2/4
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 99 times.
✗ Branch 3 not taken.
99 int result = socket_event_listen(mysql_get_socket_descriptor(mysql));
797
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 99 times.
99 if (result == -1) return 1;
798 }
799
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
112 if (status == NET_ASYNC_ERROR) {
800 return 1;
801 }
802 112 return 0;
803 112 }
804
805 112 static bool async_mysql_read_query_result_wrapper(MYSQL *mysql) {
806 net_async_status status;
807
2/4
✓ Branch 0 taken 112 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
224 AsyncTimer t(__func__);
808
3/4
✓ Branch 0 taken 21184920 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21184808 times.
✓ Branch 3 taken 112 times.
21184920 while ((status = (*mysql->methods->read_query_result_nonblocking)(mysql)) ==
809 NET_ASYNC_NOT_READY) {
810
1/2
✓ Branch 0 taken 21184808 times.
✗ Branch 1 not taken.
21184808 t.check();
811
2/4
✓ Branch 0 taken 21184808 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21184808 times.
✗ Branch 3 not taken.
21184808 int result = socket_event_listen(mysql_get_socket_descriptor(mysql));
812
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21184808 times.
21184808 if (result == -1) return true;
813 }
814
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
112 if (status == NET_ASYNC_ERROR) {
815 return true;
816 }
817 112 return false;
818 112 }
819
820 112 static int async_mysql_next_result_wrapper(MYSQL *mysql) {
821 net_async_status status;
822
2/4
✓ Branch 0 taken 112 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
224 AsyncTimer t(__func__);
823
2/4
✓ Branch 0 taken 112 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 112 times.
112 while ((status = mysql_next_result_nonblocking(mysql)) ==
824 NET_ASYNC_NOT_READY) {
825 t.check();
826 int result = socket_event_listen(mysql_get_socket_descriptor(mysql));
827 if (result == -1) return 1;
828 }
829
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
112 if (status == NET_ASYNC_ERROR)
830 return 1;
831
1/2
✓ Branch 0 taken 112 times.
✗ Branch 1 not taken.
112 else if (status == NET_ASYNC_COMPLETE_NO_MORE_RESULTS)
832 112 return -1;
833 else
834 return 0;
835 112 }
836
837 41 static MYSQL *async_mysql_real_connect_wrapper(
838 MYSQL *mysql, const char *host, const char *user, const char *passwd,
839 const char *db, uint port, const char *unix_socket, ulong client_flag) {
840 net_async_status status;
841
2/4
✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
82 AsyncTimer t(__func__);
842
843
1/2
✓ Branch 0 taken 42578 times.
✗ Branch 1 not taken.
42578 while ((status = mysql_real_connect_nonblocking(
844
2/2
✓ Branch 0 taken 42537 times.
✓ Branch 1 taken 41 times.
42578 mysql, host, user, passwd, db, port, unix_socket, client_flag)) ==
845 NET_ASYNC_NOT_READY) {
846
1/2
✓ Branch 0 taken 42537 times.
✗ Branch 1 not taken.
42537 t.check();
847 }
848
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 32 times.
41 if (status == NET_ASYNC_ERROR)
849 9 return nullptr;
850 else
851 32 return mysql;
852 41 }
853
854 static int async_mysql_query_wrapper(MYSQL *mysql, const char *query) {
855 net_async_status status;
856 AsyncTimer t(__func__);
857 while ((status = mysql_real_query_nonblocking(mysql, query, strlen(query))) ==
858 NET_ASYNC_NOT_READY) {
859 t.check();
860 int result = socket_event_listen(mysql_get_socket_descriptor(mysql));
861 if (result == -1) return 1;
862 }
863 if (status == NET_ASYNC_ERROR) {
864 return 1;
865 }
866 return 0;
867 }
868
869 110 static void async_mysql_free_result_wrapper(MYSQL_RES *result) {
870
2/4
✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 110 times.
✗ Branch 3 not taken.
220 AsyncTimer t(__func__);
871
2/4
✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 110 times.
110 while (mysql_free_result_nonblocking(result) == NET_ASYNC_NOT_READY) {
872 t.check();
873 MYSQL *mysql = result->handle;
874 int listen_result = socket_event_listen(mysql_get_socket_descriptor(mysql));
875 if (listen_result == -1) return;
876 }
877 110 return;
878 110 }
879
880 /*
881 Below are the wrapper functions which are defined on top of standard C APIs
882 to make a decision on whether to call blocking or non blocking API based on
883 --async-client option is set or not.
884 */
885 77057325 static MYSQL_ROW mysql_fetch_row_wrapper(MYSQL_RES *res) {
886
2/2
✓ Branch 0 taken 206 times.
✓ Branch 1 taken 77057119 times.
77057325 if (enable_async_client)
887 206 return async_mysql_fetch_row_wrapper(res);
888 else
889 77057119 return mysql_fetch_row(res);
890 }
891
892 10024579 static MYSQL_RES *mysql_store_result_wrapper(MYSQL *mysql) {
893
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 10024469 times.
10024579 if (enable_async_client)
894 110 return async_mysql_store_result_wrapper(mysql);
895 else
896 10024469 return mysql_store_result(mysql);
897 }
898
899 8762539 static int mysql_real_query_wrapper(MYSQL *mysql, const char *query,
900 ulong length) {
901 int rc;
902
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8762539 times.
8762539 if (0 != (rc = global_attrs->set_params(mysql))) return rc;
903
904
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 8762519 times.
8762539 if (enable_async_client)
905 20 return async_mysql_real_query_wrapper(mysql, query, length);
906 else
907 8762519 return mysql_real_query(mysql, query, length);
908 }
909
910 10409725 static int mysql_send_query_wrapper(MYSQL *mysql, const char *query,
911 ulong length) {
912 int rc;
913
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10409725 times.
10409725 if (0 != (rc = global_attrs->set_params(mysql))) return rc;
914
915
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 10409613 times.
10409725 if (enable_async_client)
916 112 return async_mysql_send_query_wrapper(mysql, query, length);
917 else
918 10409613 return mysql_send_query(mysql, query, length);
919 }
920
921 8759202 static bool mysql_read_query_result_wrapper(MYSQL *mysql) {
922 bool ret;
923
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 8759090 times.
8759202 if (enable_async_client)
924 112 ret = async_mysql_read_query_result_wrapper(mysql);
925 else
926 8759090 ret = mysql_read_query_result(mysql);
927 8759202 return ret;
928 }
929
930 11577 static int mysql_query_wrapper(MYSQL *mysql, const char *query) {
931 int rc;
932
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11577 times.
11577 if (0 != (rc = global_attrs->set_params(mysql))) return rc;
933
934
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11577 times.
11577 if (enable_async_client)
935 return async_mysql_query_wrapper(mysql, query);
936 else
937 11577 return mysql_query(mysql, query);
938 }
939
940 9509304 static int mysql_next_result_wrapper(MYSQL *mysql) {
941
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 9509192 times.
9509304 if (enable_async_client)
942 112 return async_mysql_next_result_wrapper(mysql);
943 else
944 9509192 return mysql_next_result(mysql);
945 }
946
947 121626 static MYSQL *mysql_real_connect_wrapper(MYSQL *mysql, const char *host,
948 const char *user, const char *passwd,
949 const char *db, uint port,
950 const char *unix_socket,
951 ulong client_flag) {
952
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 121585 times.
121626 if (enable_async_client)
953 41 return async_mysql_real_connect_wrapper(mysql, host, user, passwd, db, port,
954 41 unix_socket, client_flag);
955 else
956 121585 return mysql_real_connect(mysql, host, user, passwd, db, port, unix_socket,
957 121585 client_flag);
958 }
959
960 10020171 static void mysql_free_result_wrapper(MYSQL_RES *result) {
961
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 10020061 times.
10020171 if (enable_async_client)
962 110 return async_mysql_free_result_wrapper(result);
963 else
964 10020061 return mysql_free_result(result);
965 }
966
967 /* async client test code (end) */
968
969 void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, size_t len);
970 void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val);
971 void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val);
972 void dynstr_append_sorted(DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_input,
973 int start_sort_column);
974
975 void revert_properties();
976
977 74324022 void do_eval(DYNAMIC_STRING *query_eval, const char *query,
978 const char *query_end, bool pass_through_escape_chars) {
979 const char *p;
980 char c, next_c;
981 74324022 int escaped = 0;
982 VAR *v;
983
1/2
✓ Branch 0 taken 74324022 times.
✗ Branch 1 not taken.
74324022 DBUG_TRACE;
984
985
6/6
✓ Branch 0 taken 2426319676 times.
✓ Branch 1 taken 64699647 times.
✓ Branch 2 taken 2416695301 times.
✓ Branch 3 taken 9624375 times.
✓ Branch 4 taken 2416695301 times.
✓ Branch 5 taken 74324022 times.
2491019323 for (p = query; (c = *p) && p < query_end; ++p) {
986 2416695301 next_c = *(p + 1);
987
3/3
✓ Branch 0 taken 42479918 times.
✓ Branch 1 taken 638589 times.
✓ Branch 2 taken 2373576794 times.
2416695301 switch (c) {
988 42479918 case '$':
989
4/4
✓ Branch 0 taken 42411677 times.
✓ Branch 1 taken 68241 times.
✓ Branch 2 taken 42363947 times.
✓ Branch 3 taken 47730 times.
42479918 if (escaped ||
990 // a JSON path expression
991
5/6
✓ Branch 0 taken 42360463 times.
✓ Branch 1 taken 3484 times.
✓ Branch 2 taken 42360433 times.
✓ Branch 3 taken 30 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 42360433 times.
42363947 next_c == '.' || next_c == '[' || next_c == '\'' || next_c == '"') {
992 119485 escaped = 0;
993
1/2
✓ Branch 0 taken 119485 times.
✗ Branch 1 not taken.
119485 dynstr_append_mem(query_eval, p, 1);
994 } else {
995
2/4
✓ Branch 0 taken 42360433 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 42360433 times.
42360433 if (!(v = var_get(p, &p, false, false))) die("Bad variable in eval");
996
1/2
✓ Branch 0 taken 42360433 times.
✗ Branch 1 not taken.
42360433 dynstr_append_mem(query_eval, v->str_val, v->str_val_len);
997 }
998 42479918 break;
999 638589 case '\\':
1000
2/2
✓ Branch 0 taken 5090 times.
✓ Branch 1 taken 633499 times.
638589 if (escaped) {
1001 5090 escaped = 0;
1002
1/2
✓ Branch 0 taken 5090 times.
✗ Branch 1 not taken.
5090 dynstr_append_mem(query_eval, p, 1);
1003
6/6
✓ Branch 0 taken 628409 times.
✓ Branch 1 taken 5090 times.
✓ Branch 2 taken 560168 times.
✓ Branch 3 taken 68241 times.
✓ Branch 4 taken 938 times.
✓ Branch 5 taken 559230 times.
633499 } else if (next_c == '\\' || next_c == '$' || next_c == '"') {
1004 /* Set escaped only if next char is \, " or $ */
1005 74269 escaped = 1;
1006
1007
2/2
✓ Branch 0 taken 109 times.
✓ Branch 1 taken 74160 times.
74269 if (pass_through_escape_chars) {
1008 /* The escape char should be added to the output string. */
1009
1/2
✓ Branch 0 taken 109 times.
✗ Branch 1 not taken.
109 dynstr_append_mem(query_eval, p, 1);
1010 }
1011 } else
1012
1/2
✓ Branch 0 taken 559230 times.
✗ Branch 1 not taken.
559230 dynstr_append_mem(query_eval, p, 1);
1013 638589 break;
1014 2373576794 default:
1015 2373576794 escaped = 0;
1016
1/2
✓ Branch 0 taken 2373576794 times.
✗ Branch 1 not taken.
2373576794 dynstr_append_mem(query_eval, p, 1);
1017 2373576794 break;
1018 }
1019 }
1020 #ifdef _WIN32
1021 fix_win_paths(query_eval->str, query_eval->length);
1022 #endif
1023 74324022 }
1024
1025 /*
1026 Run query and dump the result to stderr in vertical format
1027
1028 NOTE! This function should be safe to call when an error
1029 has occurred and thus any further errors will be ignored(although logged)
1030
1031 SYNOPSIS
1032 show_query
1033 mysql - connection to use
1034 query - query to run
1035
1036 */
1037
1038 8 static void show_query(MYSQL *mysql, const char *query) {
1039 MYSQL_RES *res;
1040
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 DBUG_TRACE;
1041
1042
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (!mysql) return;
1043
1044
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (mysql_query_wrapper(mysql, query)) {
1045 log_msg("Error running query '%s': %d %s", query, mysql_errno(mysql),
1046 mysql_error(mysql));
1047 return;
1048 }
1049
1050
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if ((res = mysql_store_result_wrapper(mysql)) == nullptr) {
1051 /* No result set returned */
1052 return;
1053 }
1054
1055 {
1056 MYSQL_ROW row;
1057 unsigned int i;
1058 8 unsigned int row_num = 0;
1059
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 unsigned int num_fields = mysql_num_fields(res);
1060
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 MYSQL_FIELD *fields = mysql_fetch_fields(res);
1061
1062
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 fprintf(stderr, "=== %s ===\n", query);
1063
3/4
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 8 times.
40 while ((row = mysql_fetch_row_wrapper(res))) {
1064
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 unsigned long *lengths = mysql_fetch_lengths(res);
1065 32 row_num++;
1066
1067
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 fprintf(stderr, "---- %d. ----\n", row_num);
1068
2/2
✓ Branch 0 taken 438 times.
✓ Branch 1 taken 32 times.
470 for (i = 0; i < num_fields; i++) {
1069 /* looks ugly , but put here to convince parfait */
1070
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 438 times.
438 assert(lengths);
1071
1/2
✓ Branch 0 taken 438 times.
✗ Branch 1 not taken.
438 fprintf(stderr, "%s\t%.*s\n", fields[i].name, (int)lengths[i],
1072
2/2
✓ Branch 0 taken 400 times.
✓ Branch 1 taken 38 times.
438 row[i] ? row[i] : "NULL");
1073 }
1074 }
1075
3/4
✓ Branch 0 taken 198 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 198 times.
✓ Branch 3 taken 8 times.
206 for (i = 0; i < std::strlen(query) + 8; i++) fprintf(stderr, "=");
1076
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 fprintf(stderr, "\n\n");
1077 }
1078
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 mysql_free_result_wrapper(res);
1079
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 }
1080
1081 /*
1082 Show any warnings just before the error. Since the last error
1083 is added to the warning stack, only print @@warning_count-1 warnings.
1084
1085 NOTE! This function should be safe to call when an error
1086 has occurred and this any further errors will be ignored(although logged)
1087
1088 SYNOPSIS
1089 show_warnings_before_error
1090 mysql - connection to use
1091
1092 */
1093
1094 412 static void show_warnings_before_error(MYSQL *mysql) {
1095 MYSQL_RES *res;
1096 412 const char *query = "SHOW WARNINGS";
1097
1/2
✓ Branch 0 taken 412 times.
✗ Branch 1 not taken.
412 DBUG_TRACE;
1098
1099
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 412 times.
412 if (!mysql) return;
1100
1101
3/4
✓ Branch 0 taken 412 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 54 times.
✓ Branch 3 taken 358 times.
412 if (mysql_query_wrapper(mysql, query)) {
1102
3/6
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 54 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 54 times.
✗ Branch 5 not taken.
54 log_msg("Error running query '%s': %d %s", query, mysql_errno(mysql),
1103 mysql_error(mysql));
1104 54 return;
1105 }
1106
1107
2/4
✓ Branch 0 taken 358 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 358 times.
358 if ((res = mysql_store_result_wrapper(mysql)) == nullptr) {
1108 /* No result set returned */
1109 return;
1110 }
1111
1112
2/4
✓ Branch 0 taken 358 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 358 times.
358 if (mysql_num_rows(res) <= 1) {
1113 /* Don't display the last row, it's "last error" */
1114 } else {
1115 MYSQL_ROW row;
1116 unsigned int row_num = 0;
1117 unsigned int num_fields = mysql_num_fields(res);
1118
1119 fprintf(stderr, "\nWarnings from just before the error:\n");
1120 while ((row = mysql_fetch_row_wrapper(res))) {
1121 unsigned int i;
1122 unsigned long *lengths = mysql_fetch_lengths(res);
1123
1124 if (++row_num >= mysql_num_rows(res)) {
1125 /* Don't display the last row, it's "last error" */
1126 break;
1127 }
1128
1129 for (i = 0; i < num_fields; i++) {
1130 /* looks ugly , but put here to convince parfait */
1131 assert(lengths);
1132 fprintf(stderr, "%.*s ", (int)lengths[i], row[i] ? row[i] : "NULL");
1133 }
1134 fprintf(stderr, "\n");
1135 }
1136 }
1137
1/2
✓ Branch 0 taken 358 times.
✗ Branch 1 not taken.
358 mysql_free_result_wrapper(res);
1138
2/2
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 54 times.
412 }
1139
1140 enum arg_type { ARG_STRING, ARG_REST };
1141
1142 struct command_arg {
1143 const char *argname; /* Name of argument */
1144 enum arg_type type; /* Type of argument */
1145 bool required; /* Argument required */
1146 DYNAMIC_STRING *ds; /* Storage for argument */
1147 const char *description; /* Description of the argument */
1148 };
1149
1150 33003655 static void check_command_args(struct st_command *command, char *arguments,
1151 const struct command_arg *args, int num_args,
1152 const char delimiter_arg) {
1153 int i;
1154 33003655 char *ptr = arguments;
1155 const char *start;
1156
1/2
✓ Branch 0 taken 33003655 times.
✗ Branch 1 not taken.
33003655 DBUG_TRACE;
1157
3/8
✓ Branch 0 taken 33003655 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33003655 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 33003655 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
33003655 DBUG_PRINT("enter", ("num_args: %d", num_args));
1158
1159
2/2
✓ Branch 0 taken 35023382 times.
✓ Branch 1 taken 33003633 times.
68027015 for (i = 0; i < num_args; i++) {
1160 35023382 const struct command_arg *arg = &args[i];
1161 char delimiter;
1162
1163
1/3
✓ Branch 0 taken 35023382 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
35023382 switch (arg->type) {
1164 /* A string */
1165 35023382 case ARG_STRING:
1166 /* Skip leading spaces */
1167
4/4
✓ Branch 0 taken 33499379 times.
✓ Branch 1 taken 2791375 times.
✓ Branch 2 taken 1267372 times.
✓ Branch 3 taken 32232007 times.
36290754 while (*ptr && *ptr == ' ') ptr++;
1168 35023382 start = ptr;
1169 35023382 delimiter = delimiter_arg;
1170 /* If start of arg is ' ` or " search to matching quote end instead */
1171
4/4
✓ Branch 0 taken 32232007 times.
✓ Branch 1 taken 2791375 times.
✓ Branch 2 taken 16801 times.
✓ Branch 3 taken 32215206 times.
35023382 if (*ptr && strchr("'`\"", *ptr)) {
1172 16801 delimiter = *ptr;
1173 16801 start = ++ptr;
1174 }
1175 /* Find end of arg, terminated by "delimiter" */
1176
4/4
✓ Branch 0 taken 781066745 times.
✓ Branch 1 taken 33621610 times.
✓ Branch 2 taken 779664973 times.
✓ Branch 3 taken 1401772 times.
814688355 while (*ptr && *ptr != delimiter) ptr++;
1177
2/2
✓ Branch 0 taken 32172744 times.
✓ Branch 1 taken 2850638 times.
35023382 if (ptr > start) {
1178
1/2
✓ Branch 0 taken 32172744 times.
✗ Branch 1 not taken.
32172744 init_dynamic_string(arg->ds, nullptr, ptr - start);
1179
1/2
✓ Branch 0 taken 32172744 times.
✗ Branch 1 not taken.
32172744 do_eval(arg->ds, start, ptr, false);
1180 } else {
1181 /* Empty string */
1182
1/2
✓ Branch 0 taken 2850638 times.
✗ Branch 1 not taken.
2850638 init_dynamic_string(arg->ds, "", 0);
1183 }
1184 /* Find real end of arg, terminated by "delimiter_arg" */
1185 /* This will do nothing if arg was not closed by quotes */
1186
4/4
✓ Branch 0 taken 1417740 times.
✓ Branch 1 taken 33622498 times.
✓ Branch 2 taken 16856 times.
✓ Branch 3 taken 1400884 times.
35040238 while (*ptr && *ptr != delimiter_arg) ptr++;
1187
1188 35023382 command->last_argument = ptr;
1189
1190 /* Step past the delimiter */
1191
3/4
✓ Branch 0 taken 1400884 times.
✓ Branch 1 taken 33622498 times.
✓ Branch 2 taken 1400884 times.
✗ Branch 3 not taken.
35023382 if (*ptr && *ptr == delimiter_arg) ptr++;
1192
3/8
✓ Branch 0 taken 35023382 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35023382 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 35023382 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
35023382 DBUG_PRINT("info", ("val: %s", arg->ds->str));
1193 35023382 break;
1194
1195 /* Rest of line */
1196 case ARG_REST:
1197 start = ptr;
1198 init_dynamic_string(arg->ds, nullptr, command->query_len);
1199 do_eval(arg->ds, start, command->end, false);
1200 command->last_argument = command->end;
1201 DBUG_PRINT("info", ("val: %s", arg->ds->str));
1202 break;
1203
1204 default:
1205 assert("Unknown argument type");
1206 break;
1207 }
1208
1209 /* Check required arg */
1210
4/4
✓ Branch 0 taken 2850777 times.
✓ Branch 1 taken 32172605 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 2850755 times.
35023382 if (arg->ds->length == 0 && arg->required)
1211 22 die("Missing required argument '%s' to command '%.*s'", arg->argname,
1212 22 static_cast<int>(command->first_word_len), command->query);
1213 }
1214 /* Check for too many arguments passed */
1215 33003633 ptr = command->last_argument;
1216
4/4
✓ Branch 0 taken 33458567 times.
✓ Branch 1 taken 33003567 times.
✓ Branch 2 taken 33458506 times.
✓ Branch 3 taken 61 times.
66462134 while (ptr <= command->end && *ptr != '#') {
1217
4/4
✓ Branch 0 taken 954 times.
✓ Branch 1 taken 33457552 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 949 times.
33458506 if (*ptr && *ptr != ' ')
1218 5 die("Extra argument '%s' passed to '%.*s'", ptr,
1219 5 static_cast<int>(command->first_word_len), command->query);
1220 33458501 ptr++;
1221 }
1222 33003628 }
1223
1224 /// Check whether given error is in list of expected errors.
1225 ///
1226 /// @param command Pointer to the st_command structure which holds the
1227 /// arguments and information for the command.
1228 /// @param err_errno Error number of the error that actually occurred.
1229 /// @param err_sqlstate SQLSTATE that was thrown, or NULL for impossible
1230 /// (file-ops, diff, etc.)
1231 ///
1232 /// @retval -1 if the given error is not in the list, index in the
1233 /// list of expected errors otherwise.
1234 ///
1235 /// @note
1236 /// If caller needs to know whether the list was empty, they should
1237 /// check the value of expected_errors.count.
1238 1888485 static int match_expected_error(struct st_command *command,
1239 std::uint32_t err_errno,
1240 const char *err_sqlstate) {
1241 1888485 std::uint8_t index = 0;
1242
1243 // Iterator for list/vector of expected errors
1244 std::vector<std::unique_ptr<Error>>::iterator error =
1245 1888485 expected_errors->begin();
1246
1247 // Iterate over list of expected errors
1248
2/2
✓ Branch 0 taken 7177885 times.
✓ Branch 1 taken 2787 times.
7180672 for (; error != expected_errors->end(); error++) {
1249
2/2
✓ Branch 0 taken 7177859 times.
✓ Branch 1 taken 26 times.
7177885 if ((*error)->type() == ERR_ERRNO) {
1250 // Error type is ERR_ERRNO
1251
2/2
✓ Branch 0 taken 1885674 times.
✓ Branch 1 taken 5292185 times.
7177859 if ((*error)->error_code() == err_errno) return index;
1252
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 } else if ((*error)->type() == ERR_SQLSTATE) {
1253 // Error type is ERR_SQLSTATE. NULL is quite likely, but not in
1254 // conjunction with a SQL-state expect.
1255
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 25 times.
26 if (unlikely(err_sqlstate == nullptr)) {
1256 1 die("Expecting a SQLSTATE (%s) from query '%s' which cannot produce "
1257 "one.",
1258 1 (*error)->sqlstate(), command->query);
1259 }
1260
1261
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 2 times.
25 if (!std::strncmp((*error)->sqlstate(), err_sqlstate, SQLSTATE_LENGTH))
1262 23 return index;
1263 }
1264
1265 5292187 index++;
1266 }
1267
1268 2787 return -1;
1269 }
1270
1271 /// Handle errors which occurred during execution of a query.
1272 ///
1273 /// @param command Pointer to the st_command structure which holds the
1274 /// arguments and information for the command.
1275 /// @param err_errno Error number
1276 /// @param err_error Error message
1277 /// @param err_sqlstate SQLSTATE that was thrown
1278 /// @param ds Dynamic string to store the result.
1279 ///
1280 /// @note
1281 /// If there is an unexpected error, this function will abort mysqltest
1282 /// immediately.
1283 1743620 void handle_error(struct st_command *command, std::uint32_t err_errno,
1284 const char *err_error, const char *err_sqlstate,
1285 DYNAMIC_STRING *ds) {
1286
1/2
✓ Branch 0 taken 1743620 times.
✗ Branch 1 not taken.
1743620 DBUG_TRACE;
1287
1288
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1743620 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1743620 if (opt_hypergraph && err_errno == ER_HYPERGRAPH_NOT_SUPPORTED_YET) {
1289 const char errstr[] = "<ignored hypergraph optimizer error: ";
1290 dynstr_append_mem(ds, errstr, sizeof(errstr) - 1);
1291 replace_dynstr_append(ds, err_error);
1292 dynstr_append_mem(ds, ">\n", 2);
1293 revert_properties();
1294 return;
1295 }
1296
1297
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1743608 times.
1743620 if (command->abort_on_error) {
1298
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
12 if (err_errno == ER_NO_SUCH_THREAD) {
1299 /* No such thread id, let's dump the available ones */
1300
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 fprintf(stderr,
1301 "mysqltest: query '%s returned ER_NO_SUCH_THREAD, "
1302 "dumping processlist\n",
1303 command->query);
1304
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 show_query(&cur_con->mysql, "SHOW PROCESSLIST");
1305 }
1306 12 die("Query '%s' failed.\nERROR %d (%s): %s", command->query, err_errno,
1307 err_sqlstate, err_error);
1308 }
1309
1310
3/8
✓ Branch 0 taken 1743608 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1743608 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1743608 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1743608 DBUG_PRINT("info", ("Expected errors count: %zu", expected_errors->count()));
1311
1312
1/2
✓ Branch 0 taken 1743608 times.
✗ Branch 1 not taken.
1743608 int i = match_expected_error(command, err_errno, err_sqlstate);
1313
1314
2/2
✓ Branch 0 taken 1740824 times.
✓ Branch 1 taken 2784 times.
1743608 if (i >= 0) {
1315
2/2
✓ Branch 0 taken 81067 times.
✓ Branch 1 taken 1659757 times.
1740824 if (!disable_result_log) {
1316
2/2
✓ Branch 0 taken 55142 times.
✓ Branch 1 taken 25925 times.
81067 if (expected_errors->count() == 1) {
1317 // Only log error if there is one possible error
1318
1/2
✓ Branch 0 taken 55142 times.
✗ Branch 1 not taken.
55142 dynstr_append_mem(ds, "ERROR ", 6);
1319
1/2
✓ Branch 0 taken 55142 times.
✗ Branch 1 not taken.
55142 replace_dynstr_append(ds, err_sqlstate);
1320
1/2
✓ Branch 0 taken 55142 times.
✗ Branch 1 not taken.
55142 dynstr_append_mem(ds, ": ", 2);
1321
1/2
✓ Branch 0 taken 55142 times.
✗ Branch 1 not taken.
55142 replace_dynstr_append(ds, err_error);
1322
1/2
✓ Branch 0 taken 55142 times.
✗ Branch 1 not taken.
55142 dynstr_append_mem(ds, "\n", 1);
1323 }
1324 // Don't log error if we may not get an error
1325
4/6
✓ Branch 0 taken 25925 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25925 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2068 times.
✓ Branch 5 taken 23857 times.
77775 else if (expected_errors->type() == ERR_SQLSTATE ||
1326
2/2
✓ Branch 0 taken 2068 times.
✓ Branch 1 taken 23857 times.
51850 (expected_errors->type() == ERR_ERRNO &&
1327 25925 expected_errors->error_code() != 0))
1328
1/2
✓ Branch 0 taken 2068 times.
✗ Branch 1 not taken.
2068 dynstr_append(ds, "Got one of the listed errors\n");
1329 }
1330
1331
1/2
✓ Branch 0 taken 1740824 times.
✗ Branch 1 not taken.
1740824 revert_properties();
1332 1740824 return;
1333 }
1334
1335
3/8
✓ Branch 0 taken 2784 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2784 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2784 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
2784 DBUG_PRINT("info",
1336 ("i: %d Expected errors count: %zu", i, expected_errors->count()));
1337
1338
2/2
✓ Branch 0 taken 2627 times.
✓ Branch 1 taken 157 times.
2784 if (!disable_result_log) {
1339
1/2
✓ Branch 0 taken 2627 times.
✗ Branch 1 not taken.
2627 dynstr_append_mem(ds, "ERROR ", 6);
1340
1/2
✓ Branch 0 taken 2627 times.
✗ Branch 1 not taken.
2627 replace_dynstr_append(ds, err_sqlstate);
1341
1/2
✓ Branch 0 taken 2627 times.
✗ Branch 1 not taken.
2627 dynstr_append_mem(ds, ": ", 2);
1342
1/2
✓ Branch 0 taken 2627 times.
✗ Branch 1 not taken.
2627 replace_dynstr_append(ds, err_error);
1343
1/2
✓ Branch 0 taken 2627 times.
✗ Branch 1 not taken.
2627 dynstr_append_mem(ds, "\n", 1);
1344 }
1345
1346
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2777 times.
2784 if (expected_errors->count()) {
1347
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (err_errno == ER_NO_SUCH_THREAD) {
1348 /* No such thread id, let's dump the available ones */
1349
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 fprintf(stderr,
1350 "mysqltest: query '%s returned ER_NO_SUCH_THREAD, "
1351 "dumping processlist\n",
1352 command->query);
1353
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 show_query(&cur_con->mysql, "SHOW PROCESSLIST");
1354 }
1355
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
7 if (expected_errors->count() == 1) {
1356 4 die("Query '%s' failed with wrong error %d: '%s', should have failed "
1357 "with error '%s'.",
1358 command->query, err_errno, err_error,
1359
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 expected_errors->error_list().c_str());
1360 } else {
1361 3 die("Query '%s' failed with wrong error %d: '%s', should have failed "
1362 "with any of '%s' errors.",
1363 command->query, err_errno, err_error,
1364
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 expected_errors->error_list().c_str());
1365 }
1366 }
1367
1368
1/2
✓ Branch 0 taken 2777 times.
✗ Branch 1 not taken.
2777 revert_properties();
1369
2/2
✓ Branch 0 taken 2777 times.
✓ Branch 1 taken 1740824 times.
1743601 }
1370
1371 /// Handle absence of errors after execution.
1372 ///
1373 /// Abort test run if the query succeeds and was expected to fail with
1374 /// an error.
1375 ///
1376 /// @param command Pointer to the st_command structure which holds the
1377 /// arguments and information for the command.
1378 8710847 void handle_no_error(struct st_command *command) {
1379
1/2
✓ Branch 0 taken 8710847 times.
✗ Branch 1 not taken.
8710847 DBUG_TRACE;
1380
1381
2/2
✓ Branch 0 taken 109480 times.
✓ Branch 1 taken 8601367 times.
8710847 if (expected_errors->count()) {
1382
1/2
✓ Branch 0 taken 109480 times.
✗ Branch 1 not taken.
109480 int index = match_expected_error(command, 0, "00000");
1383
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 109479 times.
109480 if (index == -1) {
1384
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (expected_errors->count() == 1) {
1385 1 die("Query '%s' succeeded, should have failed with error '%s'",
1386
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 command->query, expected_errors->error_list().c_str());
1387 } else {
1388 die("Query '%s' succeeded, should have failed with any of '%s' errors.",
1389 command->query, expected_errors->error_list().c_str());
1390 }
1391 }
1392 }
1393 8710846 }
1394
1395 /// Save error code returned by a mysqltest command in '$__error'
1396 /// variable.
1397 ///
1398 /// @param error Error code
1399 430243 static void save_error_code(std::uint32_t error) {
1400 char error_value[10];
1401 430243 size_t error_length = std::snprintf(error_value, 10, "%u", error);
1402 430243 error_value[error_length > 9 ? 9 : error_length] = '0';
1403 430243 const char *var_name = "__error";
1404
1/2
✓ Branch 0 taken 430243 times.
✗ Branch 1 not taken.
430243 var_set(var_name, var_name + 7, error_value, error_value + error_length);
1405 430243 }
1406
1407 /// Handle errors which occurred during execution of mysqltest commands
1408 /// like 'move_file', 'remove_file' etc which are used to perform file
1409 /// system operations.
1410 ///
1411 /// @param command Pointer to the st_command structure which holds the
1412 /// arguments and information for the command.
1413 /// @param error Error number
1414 ///
1415 /// @note
1416 /// If there is an unexpected error, this function will abort mysqltest
1417 /// client immediately.
1418 392157 static void handle_command_error(struct st_command *command,
1419 std::uint32_t error) {
1420
1/2
✓ Branch 0 taken 392157 times.
✗ Branch 1 not taken.
392157 DBUG_TRACE;
1421
3/8
✓ Branch 0 taken 392157 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 392157 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 392157 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
392157 DBUG_PRINT("enter", ("error: %d", error));
1422
1423
4/4
✓ Branch 0 taken 35006 times.
✓ Branch 1 taken 357151 times.
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 34975 times.
392157 if (error != 0 && command->abort_on_error) {
1424
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 die("Command \"%s\" failed with error %d. my_errno=%d.",
1425 31 command_names[command->type - 1], error, my_errno());
1426 }
1427
1428
2/2
✓ Branch 0 taken 35374 times.
✓ Branch 1 taken 356752 times.
392126 if (expected_errors->count()) {
1429
1/2
✓ Branch 0 taken 35373 times.
✗ Branch 1 not taken.
35374 int index = match_expected_error(command, error, nullptr);
1430
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 35371 times.
35373 if (index == -1) {
1431
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (error != 0) {
1432 if (expected_errors->count() == 1) {
1433 die("Command \"%s\" failed with wrong error: %d, my_errno=%d. should "
1434 "have failed with error '%s'.",
1435 command_names[command->type - 1], error, my_errno(),
1436 expected_errors->error_list().c_str());
1437 } else {
1438 die("Command \"%s\" failed with wrong error: %d, my_errno=%d. should "
1439 "have failed with any of '%s' errors.",
1440 command_names[command->type - 1], error, my_errno(),
1441 expected_errors->error_list().c_str());
1442 }
1443 } else {
1444 // Command succeeded, should have failed with an error
1445
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (expected_errors->count() == 1) {
1446 2 die("Command \"%s\" succeeded, should have failed with error '%s'.",
1447 2 command_names[command->type - 1],
1448
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 expected_errors->error_list().c_str());
1449 } else {
1450 die("Command \"%s\" succeeded, should have failed with any of '%s' "
1451 "errors.",
1452 command_names[command->type - 1],
1453 expected_errors->error_list().c_str());
1454 }
1455 }
1456 }
1457 }
1458
1459 // Save the error code
1460
1/2
✓ Branch 0 taken 392123 times.
✗ Branch 1 not taken.
392123 save_error_code(error);
1461
1462
1/2
✓ Branch 0 taken 392123 times.
✗ Branch 1 not taken.
392123 revert_properties();
1463 392123 }
1464
1465 55506 static void close_connections() {
1466
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 DBUG_TRACE;
1467
2/2
✓ Branch 0 taken 98962 times.
✓ Branch 1 taken 55506 times.
154468 for (--next_con; next_con >= connections; --next_con) {
1468
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 98962 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
98962 if (next_con->stmt) mysql_stmt_close(next_con->stmt);
1469 98962 next_con->stmt = nullptr;
1470
1/2
✓ Branch 0 taken 98962 times.
✗ Branch 1 not taken.
98962 mysql_close(&next_con->mysql);
1471
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 98962 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
98962 if (next_con->util_mysql) mysql_close(next_con->util_mysql);
1472
1/2
✓ Branch 0 taken 98962 times.
✗ Branch 1 not taken.
98962 my_free(next_con->name);
1473 }
1474
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 my_free(connections);
1475 55506 }
1476
1477 13277 static void close_statements() {
1478 struct st_connection *con;
1479
1/2
✓ Branch 0 taken 13277 times.
✗ Branch 1 not taken.
13277 DBUG_TRACE;
1480
2/2
✓ Branch 0 taken 99572 times.
✓ Branch 1 taken 13277 times.
112849 for (con = connections; con < next_con; con++) {
1481
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 99572 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
99572 if (con->stmt) mysql_stmt_close(con->stmt);
1482 99572 con->stmt = nullptr;
1483 }
1484 13277 }
1485
1486 55512 static void close_files() {
1487
1/2
✓ Branch 0 taken 55512 times.
✗ Branch 1 not taken.
55512 DBUG_TRACE;
1488
2/2
✓ Branch 0 taken 56842 times.
✓ Branch 1 taken 55512 times.
112354 for (; cur_file >= file_stack; cur_file--) {
1489
4/4
✓ Branch 0 taken 16804 times.
✓ Branch 1 taken 40038 times.
✓ Branch 2 taken 16549 times.
✓ Branch 3 taken 255 times.
56842 if (cur_file->file && cur_file->file != stdin) {
1490
3/8
✓ Branch 0 taken 16549 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16549 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 16549 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
16549 DBUG_PRINT("info", ("closing file: %s", cur_file->file_name));
1491
1/2
✓ Branch 0 taken 16549 times.
✗ Branch 1 not taken.
16549 fclose(cur_file->file);
1492 }
1493
1/2
✓ Branch 0 taken 56842 times.
✗ Branch 1 not taken.
56842 my_free(cur_file->file_name);
1494 56842 cur_file->file_name = nullptr;
1495 }
1496 55512 }
1497
1498 55512 static void free_used_memory() {
1499 // Delete the expected errors pointer
1500
1/2
✓ Branch 0 taken 55512 times.
✗ Branch 1 not taken.
55512 delete expected_errors;
1501
1502 // Delete disabled and enabled warning list
1503
1/2
✓ Branch 0 taken 55512 times.
✗ Branch 1 not taken.
55512 delete disabled_warnings;
1504
1/2
✓ Branch 0 taken 55512 times.
✗ Branch 1 not taken.
55512 delete enabled_warnings;
1505
1506
2/2
✓ Branch 0 taken 55506 times.
✓ Branch 1 taken 6 times.
55512 if (connections) close_connections();
1507 55512 close_files();
1508
1/2
✓ Branch 0 taken 55512 times.
✗ Branch 1 not taken.
55512 delete var_hash;
1509 55512 var_hash = nullptr;
1510
1511 struct st_command **q;
1512
2/2
✓ Branch 0 taken 441208644 times.
✓ Branch 1 taken 55512 times.
441264156 for (q = q_lines->begin(); q != q_lines->end(); ++q) {
1513 441208644 my_free((*q)->query_buf);
1514
2/2
✓ Branch 0 taken 481607 times.
✓ Branch 1 taken 440727037 times.
441208644 if ((*q)->content.str) dynstr_free(&(*q)->content);
1515 441208644 my_free((*q));
1516 }
1517
1518
2/2
✓ Branch 0 taken 555120 times.
✓ Branch 1 taken 55512 times.
610632 for (size_t i = 0; i < 10; i++) {
1519
2/2
✓ Branch 0 taken 178 times.
✓ Branch 1 taken 554942 times.
555120 if (var_reg[i].alloced_len) my_free(var_reg[i].str_val);
1520 }
1521
1522
1/2
✓ Branch 0 taken 55512 times.
✗ Branch 1 not taken.
55512 delete q_lines;
1523 55512 dynstr_free(&ds_res);
1524 55512 dynstr_free(&ds_result);
1525
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 55493 times.
55512 if (ds_warn) dynstr_free(ds_warn);
1526 55512 free_all_replace();
1527 55512 my_free(opt_pass);
1528 #ifdef _WIN32
1529 free_win_path_patterns();
1530 #endif
1531
1532
1/2
✓ Branch 0 taken 55512 times.
✗ Branch 1 not taken.
55512 if (global_attrs != nullptr) {
1533
1/2
✓ Branch 0 taken 55512 times.
✗ Branch 1 not taken.
55512 delete global_attrs;
1534 55512 global_attrs = nullptr;
1535 }
1536
1537 // Only call mysql_server_end if mysql_server_init has been called.
1538
2/2
✓ Branch 0 taken 55506 times.
✓ Branch 1 taken 6 times.
55512 if (server_initialized) mysql_server_end();
1539
1540 // Don't use DBUG after mysql_server_end()
1541 55512 return;
1542 }
1543
1544 55512 static void cleanup_and_exit(int exit_code) {
1545
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55512 times.
55512 if (opt_offload_count_file) {
1546 // Check if the current connection is active, if not create one.
1547 if (cur_con->mysql.net.vio == nullptr) {
1548 mysql_real_connect(&cur_con->mysql, opt_host, opt_user, opt_pass, opt_db,
1549 opt_port, unix_sock,
1550 CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS);
1551 }
1552
1553 // Save the final value of secondary engine execution status.
1554 if (secondary_engine->offload_count(&cur_con->mysql, "after"))
1555 exit_code = 1;
1556 secondary_engine->report_offload_count(opt_offload_count_file);
1557 }
1558
1559 55512 free_used_memory();
1560 55512 my_end(my_end_arg);
1561
1562 enum test_exit_code { PASS, FAIL, SKIPPED = 62, NOSKIP_PASS, NOSKIP_FAIL };
1563
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 55511 times.
55512 if (skip_ignored) {
1564
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 exit_code = (exit_code == PASS) ? NOSKIP_PASS : NOSKIP_FAIL;
1565 }
1566
1567
2/2
✓ Branch 0 taken 43518 times.
✓ Branch 1 taken 11994 times.
55512 if (!silent) {
1568
3/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 29602 times.
✓ Branch 2 taken 13915 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
43518 switch (exit_code) {
1569 1 case FAIL:
1570 1 printf("not ok\n");
1571 1 break;
1572 29602 case PASS:
1573 29602 printf("ok\n");
1574 29602 break;
1575 13915 case SKIPPED:
1576 13915 printf("skipped\n");
1577 13915 break;
1578 case NOSKIP_PASS:
1579 printf("noskip-passed\n");
1580 break;
1581 case NOSKIP_FAIL:
1582 printf("noskip-failed\n");
1583 break;
1584 default:
1585 printf("unknown exit code: %d\n", exit_code);
1586 assert(0);
1587 }
1588 }
1589
1590 // exit() appears to be not 100% reliable on Windows under some conditions.
1591 #ifdef _WIN32
1592 if (opt_safe_process_pid) {
1593 // Close the stack trace request event handle
1594 if (stacktrace_request_event != NULL) CloseHandle(stacktrace_request_event);
1595
1596 // Detach or stop the thread waiting for stack trace event to occur.
1597 if (wait_for_stacktrace_request_event_thread.joinable())
1598 wait_for_stacktrace_request_event_thread.detach();
1599
1600 // Close the thread handle
1601 if (!CloseHandle(mysqltest_thread))
1602 die("CloseHandle failed, err = %d.\n", GetLastError());
1603 }
1604
1605 fflush(stdout);
1606 fflush(stderr);
1607 _exit(exit_code);
1608 #else
1609 55512 exit(exit_code);
1610 #endif
1611 }
1612
1613 15128 static void print_file_stack() {
1614 15128 fprintf(stderr, "file %s: %d\n", cur_file->file_name, cur_file->lineno);
1615
1616 struct st_test_file *err_file;
1617
2/2
✓ Branch 0 taken 1330 times.
✓ Branch 1 taken 15128 times.
16458 for (err_file = cur_file - 1; err_file >= file_stack; err_file--) {
1618 1330 fprintf(stderr, "included from %s: %d\n", err_file->file_name,
1619 err_file->lineno);
1620 }
1621 15128 }
1622
1623 476 void die(const char *fmt, ...) {
1624 static int dying = 0;
1625 va_list args;
1626
3/8
✓ Branch 0 taken 476 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 476 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 476 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
476 DBUG_PRINT("enter", ("start_lineno: %d", start_lineno));
1627
1628 // Flush whatever was printed by the failing command before it died.
1629
1/2
✓ Branch 0 taken 476 times.
✗ Branch 1 not taken.
476 flush_ds_res();
1630
1631 // Protect against dying twice first time 'die' is called, try to
1632 // write log files second time, just exit.
1633
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 476 times.
476 if (dying) cleanup_and_exit(1);
1634 476 dying = 1;
1635
1636 // Print the error message
1637
1/2
✓ Branch 0 taken 476 times.
✗ Branch 1 not taken.
476 fprintf(stderr, "mysqltest: ");
1638
1639
3/4
✓ Branch 0 taken 404 times.
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 404 times.
✗ Branch 3 not taken.
476 if (start_lineno > 0) fprintf(stderr, "At line %u: ", start_lineno);
1640
1641
1/2
✓ Branch 0 taken 476 times.
✗ Branch 1 not taken.
476 if (fmt) {
1642 476 va_start(args, fmt);
1643
1/2
✓ Branch 0 taken 476 times.
✗ Branch 1 not taken.
476 vfprintf(stderr, fmt, args);
1644
1/2
✓ Branch 0 taken 476 times.
✗ Branch 1 not taken.
476 fprintf(stderr, "\n");
1645 476 va_end(args);
1646 } else
1647 fprintf(stderr, "Unknown error");
1648
1649 // Print the file stack
1650
3/4
✓ Branch 0 taken 476 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 134 times.
✓ Branch 3 taken 342 times.
476 if (cur_file && cur_file != file_stack) {
1651
1/2
✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
134 fprintf(stderr, "In included ");
1652
1/2
✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
134 print_file_stack();
1653 }
1654
1655
1/2
✓ Branch 0 taken 476 times.
✗ Branch 1 not taken.
476 fflush(stderr);
1656
1657
3/4
✓ Branch 0 taken 176 times.
✓ Branch 1 taken 300 times.
✓ Branch 2 taken 176 times.
✗ Branch 3 not taken.
476 if (result_file_name) log_file.show_tail(opt_tail_lines);
1658
1659 // Help debugging by displaying any warnings that might have
1660 // been produced prior to the error.
1661
5/6
✓ Branch 0 taken 424 times.
✓ Branch 1 taken 52 times.
✓ Branch 2 taken 412 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 412 times.
✗ Branch 5 not taken.
476 if (cur_con && !cur_con->pending) show_warnings_before_error(&cur_con->mysql);
1662
1663 476 cleanup_and_exit(1);
1664 }
1665
1666 14994 void abort_not_supported_test(const char *fmt, ...) {
1667 va_list args;
1668
1/2
✓ Branch 0 taken 14994 times.
✗ Branch 1 not taken.
14994 DBUG_TRACE;
1669
1670 /* Print include filestack */
1671
1/2
✓ Branch 0 taken 14994 times.
✗ Branch 1 not taken.
14994 fprintf(stderr, "The test '%s' is not supported by this installation\n",
1672 file_stack->file_name);
1673
1/2
✓ Branch 0 taken 14994 times.
✗ Branch 1 not taken.
14994 fprintf(stderr, "Detected in ");
1674
1/2
✓ Branch 0 taken 14994 times.
✗ Branch 1 not taken.
14994 print_file_stack();
1675
1676 /* Print error message */
1677 14994 va_start(args, fmt);
1678
1/2
✓ Branch 0 taken 14994 times.
✗ Branch 1 not taken.
14994 if (fmt) {
1679
1/2
✓ Branch 0 taken 14994 times.
✗ Branch 1 not taken.
14994 fprintf(stderr, "reason: ");
1680
1/2
✓ Branch 0 taken 14994 times.
✗ Branch 1 not taken.
14994 vfprintf(stderr, fmt, args);
1681
1/2
✓ Branch 0 taken 14994 times.
✗ Branch 1 not taken.
14994 fprintf(stderr, "\n");
1682
1/2
✓ Branch 0 taken 14994 times.
✗ Branch 1 not taken.
14994 fflush(stderr);
1683 }
1684 14994 va_end(args);
1685
1686 14994 cleanup_and_exit(62);
1687 }
1688
1689 376544 void verbose_msg(const char *fmt, ...) {
1690 va_list args;
1691
1/2
✓ Branch 0 taken 376544 times.
✗ Branch 1 not taken.
376544 DBUG_TRACE;
1692
2/2
✓ Branch 0 taken 169330 times.
✓ Branch 1 taken 207214 times.
376544 if (!verbose) return;
1693
1694 207214 va_start(args, fmt);
1695
1/2
✓ Branch 0 taken 207214 times.
✗ Branch 1 not taken.
207214 fprintf(stderr, "mysqltest: ");
1696
2/4
✓ Branch 0 taken 207214 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 207214 times.
207214 if (cur_file && cur_file != file_stack)
1697 fprintf(stderr, "In included file \"%s\": ", cur_file->file_name);
1698
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 207214 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
207214 if (start_lineno != 0) fprintf(stderr, "At line %u: ", start_lineno);
1699
1/2
✓ Branch 0 taken 207214 times.
✗ Branch 1 not taken.
207214 vfprintf(stderr, fmt, args);
1700
1/2
✓ Branch 0 taken 207214 times.
✗ Branch 1 not taken.
207214 fprintf(stderr, "\n");
1701 207214 va_end(args);
1702
2/2
✓ Branch 0 taken 207214 times.
✓ Branch 1 taken 169330 times.
376544 }
1703
1704 130 void log_msg(const char *fmt, ...) {
1705 va_list args;
1706 char buff[1024];
1707 size_t len;
1708
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
130 DBUG_TRACE;
1709
1710 130 va_start(args, fmt);
1711 130 len = vsnprintf(buff, sizeof(buff) - 1, fmt, args);
1712 130 va_end(args);
1713
1714
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
130 dynstr_append_mem(&ds_res, buff, len);
1715
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
130 dynstr_append(&ds_res, "\n");
1716 130 }
1717
1718 2830802749 void flush_ds_res() {
1719
2/2
✓ Branch 0 taken 3354552 times.
✓ Branch 1 taken 2827448197 times.
2830802749 if (ds_res.length) {
1720
3/6
✓ Branch 0 taken 3354552 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3354552 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3354552 times.
3354552 if (log_file.write(ds_res.str, ds_res.length) || log_file.flush())
1721 cleanup_and_exit(1);
1722
1723
2/2
✓ Branch 0 taken 27932 times.
✓ Branch 1 taken 3326620 times.
3354552 if (!result_file_name) {
1724
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27932 times.
27932 if (std::fwrite(ds_res.str, 1, ds_res.length, stdout) != ds_res.length)
1725 cleanup_and_exit(1);
1726 27932 std::fflush(stdout);
1727 }
1728
1729 3354552 dynstr_set(&ds_res, nullptr);
1730 }
1731 2830802749 }
1732
1733 /*
1734 Read a file and append it to ds
1735
1736 SYNOPSIS
1737 cat_file
1738 ds - pointer to dynamic string where to add the files content
1739 filename - name of the file to read
1740
1741 */
1742
1743 13118 static int cat_file(DYNAMIC_STRING *ds, const char *filename) {
1744 int fd;
1745 size_t len;
1746 char buff[512];
1747 13118 bool dangling_cr = false;
1748
1749
3/4
✓ Branch 0 taken 13118 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 13114 times.
13118 if ((fd = my_open(filename, O_RDONLY, MYF(0))) < 0) return 1;
1750
1751 13114 std::string file_content;
1752
1753
3/4
✓ Branch 0 taken 24006 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10892 times.
✓ Branch 3 taken 13114 times.
24006 while ((len = my_read(fd, (uchar *)&buff, sizeof(buff), MYF(0))) > 0) {
1754 10892 char *p = buff, *start = buff;
1755
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10892 times.
10892 if (dangling_cr) {
1756 if (*p != '\n') file_content.append("\r");
1757 dangling_cr = false;
1758 }
1759
2/2
✓ Branch 0 taken 3763845 times.
✓ Branch 1 taken 10892 times.
3774737 while (p < buff + len) {
1760 /* Convert cr/lf to lf */
1761
4/6
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3763841 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
3763845 if (*p == '\r' && *(p + 1) && *(p + 1) == '\n') {
1762 /* Add fake newline instead of cr and output the line */
1763 4 *p = '\n';
1764 4 p++; /* Step past the "fake" newline */
1765
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 file_content.append(start, p - start);
1766 4 p++; /* Step past the "fake" newline */
1767 4 start = p;
1768 } else
1769 3763841 p++;
1770 }
1771
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10892 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
10892 if (*(p - 1) == '\r' && len == 512) dangling_cr = true;
1772 size_t buf_len;
1773 /* Add any characters that might be left */
1774
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10892 times.
10892 if (dangling_cr)
1775 buf_len = p - start - 1;
1776 else
1777 10892 buf_len = p - start;
1778
1/2
✓ Branch 0 taken 10892 times.
✗ Branch 1 not taken.
10892 file_content.append(start, buf_len);
1779 }
1780
1781
1/2
✓ Branch 0 taken 13114 times.
✗ Branch 1 not taken.
13114 replace_dynstr_append(ds, file_content.c_str());
1782
1/2
✓ Branch 0 taken 13114 times.
✗ Branch 1 not taken.
13114 my_close(fd, MYF(0));
1783
1784 13114 return 0;
1785 13114 }
1786
1787 /*
1788 Run the specified command with popen
1789
1790 SYNOPSIS
1791 run_command
1792 cmd - command to execute(should be properly quoted
1793 ds_res- pointer to dynamic string where to store the result
1794
1795 */
1796
1797 15 static int run_command(char *cmd, DYNAMIC_STRING *ds_res) {
1798 15 char buf[512] = {0};
1799 FILE *res_file;
1800 int error;
1801
1802
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 if (!(res_file = popen(cmd, "r"))) die("popen(\"%s\", \"r\") failed", cmd);
1803
1804
3/4
✓ Branch 0 taken 365 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 350 times.
✓ Branch 3 taken 15 times.
365 while (fgets(buf, sizeof(buf), res_file)) {
1805
3/8
✓ Branch 0 taken 350 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 350 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 350 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
350 DBUG_PRINT("info", ("buf: %s", buf));
1806
1/2
✓ Branch 0 taken 350 times.
✗ Branch 1 not taken.
350 if (ds_res) {
1807 /* Save the output of this command in the supplied string */
1808
1/2
✓ Branch 0 taken 350 times.
✗ Branch 1 not taken.
350 dynstr_append(ds_res, buf);
1809 } else {
1810 /* Print it directly on screen */
1811 fprintf(stdout, "%s", buf);
1812 }
1813 }
1814
1815
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 error = pclose(res_file);
1816 15 return WEXITSTATUS(error);
1817 }
1818
1819 /*
1820 Run the specified tool with variable number of arguments
1821
1822 SYNOPSIS
1823 run_tool
1824 tool_path - the name of the tool to run
1825 ds_res - pointer to dynamic string where to store the result
1826 ... - variable number of arguments that will be properly
1827 quoted and appended after the tool's name
1828
1829 */
1830
1831 15 static int run_tool(const char *tool_path, DYNAMIC_STRING *ds_res, ...) {
1832 int ret;
1833 const char *arg;
1834 va_list args;
1835 DYNAMIC_STRING ds_cmdline;
1836
1837
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 DBUG_TRACE;
1838
3/8
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
15 DBUG_PRINT("enter", ("tool_path: %s", tool_path));
1839
1840
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 if (init_dynamic_string(&ds_cmdline, IF_WIN("\"", ""), FN_REFLEN))
1841 die("Out of memory");
1842
1843
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 dynstr_append_os_quoted(&ds_cmdline, tool_path, NullS);
1844
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 dynstr_append(&ds_cmdline, " ");
1845
1846 15 va_start(args, ds_res);
1847
1848
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 15 times.
75 while ((arg = va_arg(args, char *))) {
1849 /* Options should be os quoted */
1850
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (std::strncmp(arg, "--", 2) == 0)
1851 dynstr_append_os_quoted(&ds_cmdline, arg, NullS);
1852 else
1853
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
60 dynstr_append(&ds_cmdline, arg);
1854
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
60 dynstr_append(&ds_cmdline, " ");
1855 }
1856
1857 15 va_end(args);
1858
1859 #ifdef _WIN32
1860 dynstr_append(&ds_cmdline, "\"");
1861 #endif
1862
1863
3/8
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
15 DBUG_PRINT("info", ("Running: %s", ds_cmdline.str));
1864
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 ret = run_command(ds_cmdline.str, ds_res);
1865
3/8
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
15 DBUG_PRINT("exit", ("ret: %d", ret));
1866
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 dynstr_free(&ds_cmdline);
1867 15 return ret;
1868 15 }
1869
1870 /*
1871 Test if diff is present. This is needed on Windows systems
1872 as the OS returns 1 whether diff is successful or if it is
1873 not present.
1874
1875 We run diff -v and look for output in stdout.
1876 We don't redirect stderr to stdout to make for a simplified check
1877 Windows will output '"diff"' is not recognized... to stderr if it is
1878 not present.
1879 */
1880
1881 #ifdef _WIN32
1882
1883 static int diff_check(const char *diff_name) {
1884 FILE *res_file;
1885 char buf[128];
1886 int have_diff = 0;
1887
1888 snprintf(buf, sizeof(buf), "%s -v", diff_name);
1889
1890 if (!(res_file = popen(buf, "r"))) die("popen(\"%s\", \"r\") failed", buf);
1891
1892 /* if diff is not present, nothing will be in stdout to increment have_diff */
1893 if (fgets(buf, sizeof(buf), res_file)) have_diff = 1;
1894
1895 pclose(res_file);
1896
1897 return have_diff;
1898 }
1899
1900 #endif
1901
1902 /**
1903 See if the diff consists _solely_ of hunks that are due to output lines being
1904 replaced with the special hypergraph error text. If so, return true.
1905 An example hunk:
1906
1907 @@ -315,45 +211,25 @@
1908 SELECT * FROM articles WHERE MATCH(title, body) AGAINST('a' IN BOOLEAN MODE);
1909 -id title body
1910 -3 foo bar
1911 -2 bar baz
1912 +<ignored hypergraph optimizer error: The hypergraph optimizer does not yet
1913 support 'fulltext search'> SELECT ...
1914 */
1915 static bool is_diff_clean_except_hypergraph(DYNAMIC_STRING *ds) {
1916 constexpr char error_signature[] = "+<ignored hypergraph optimizer error: ";
1917
1918 enum STATE {
1919 LOOKING_FOR_FIRST_HUNK,
1920 IN_HUNK,
1921 FOUND_NEGATIVE_LINE
1922 } state = LOOKING_FOR_FIRST_HUNK;
1923
1924 const char *end = ds->str + ds->length;
1925 const char *line_end;
1926 for (const char *ptr = ds->str; ptr < end; ptr = line_end + 1) {
1927 line_end = pointer_cast<const char *>(memchr(ptr, '\n', end - ptr));
1928 if (line_end == nullptr) {
1929 line_end = end;
1930 }
1931
1932 size_t line_length = line_end - ptr;
1933 if (state == LOOKING_FOR_FIRST_HUNK) {
1934 if (line_length < 2) {
1935 // Malformed diff, give up.
1936 return false;
1937 }
1938 if (ptr[0] == '@' && ptr[1] == '@') {
1939 // Beginning of hunk.
1940 state = IN_HUNK;
1941 } else {
1942 // Still waiting for the first hunk.
1943 }
1944 } else {
1945 assert(state == IN_HUNK || state == FOUND_NEGATIVE_LINE);
1946
1947 if (line_length < 1) {
1948 // Malformed diff, give up.
1949 return false;
1950 }
1951 if (ptr[0] == '-') {
1952 // A group of negative diff lines (either the beginning, or more
1953 // of them). Hopefully, this group will be ended with an instance of
1954 // "+<ignored hypergraph optimizer ...", which means we can ignore it.
1955 state = FOUND_NEGATIVE_LINE;
1956 } else if (ptr[0] == ' ') {
1957 if (state == IN_HUNK) {
1958 // Waiting for the next (or first) diff. Continue.
1959 } else {
1960 // Negative lines that were not followed by an ignored error.
1961 // These are not acceptable.
1962 return false;
1963 }
1964 } else if (line_length >= 2 && ptr[0] == '@' && ptr[1] == '@') {
1965 // Beginning of a new hunk.
1966 if (state == FOUND_NEGATIVE_LINE) {
1967 // Negative lines that were not followed by an ignored error.
1968 // These are not acceptable.
1969 return false;
1970 }
1971 } else if (ptr[0] == '+') {
1972 if (line_length >= strlen(error_signature) &&
1973 memcmp(ptr, error_signature, strlen(error_signature)) == 0) {
1974 // An ignored error. Whether there were previous negative lines
1975 // or not, that's fine; we can end and ignore this group.
1976 state = IN_HUNK;
1977 } else {
1978 // A non-ignored error.
1979 return false;
1980 }
1981 } else {
1982 // Malformed diff, give up.
1983 return false;
1984 }
1985 }
1986 }
1987
1988 if (state == FOUND_NEGATIVE_LINE) {
1989 // Negative lines that were not followed by an ignored error.
1990 // These are not acceptable.
1991 return false;
1992 }
1993
1994 // Found nothing else, so success!
1995 return true;
1996 }
1997
1998 /// Show the diff of two files using the systems builtin diff
1999 /// command. If no such diff command exist, just dump the content
2000 /// of the two files and inform about how to get "diff"
2001 ///
2002 /// @param ds Pointer to dynamic string where to add the
2003 /// diff. If NULL, print the diff to stderr.
2004 /// @param filename1 Name of the first file
2005 /// @param filename2 Name of the second file
2006 /// @return true if the diff should be ignored
2007 15 static bool show_diff(DYNAMIC_STRING *ds, const char *filename1,
2008 const char *filename2) {
2009 DYNAMIC_STRING ds_diff;
2010
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 if (init_dynamic_string(&ds_diff, "", 256)) die("Out of memory");
2011
2012 15 const char *diff_name = nullptr;
2013
2014 // Determine if we have diff on Windows. If yes, then needs special
2015 // processing due to return values on that OS. This test is only done
2016 // on Windows since it's only needed there in order to correctly
2017 // detect non-availibility of 'diff', and the way it's implemented
2018 // does not work with default 'diff' on Solaris.
2019 #ifdef _WIN32
2020 if (diff_check("diff"))
2021 diff_name = "diff";
2022 else if (diff_check("mtrdiff"))
2023 diff_name = "mtrdiff";
2024 else
2025 diff_name = 0;
2026 #else
2027 // Otherwise always assume it's called diff
2028 15 diff_name = "diff";
2029 #endif
2030
2031
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (diff_name) {
2032 15 int exit_code = 0;
2033 // Use 'diff --color=always' to print the colored diff if it is enabled
2034
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (opt_colored_diff) {
2035 // Most "diff" tools return '> 1' if error
2036 exit_code = run_tool(diff_name, &ds_diff, "-u --color='always'",
2037 filename1, filename2, "2>&1", NULL);
2038
2039 if (exit_code > 1)
2040 die("Option '--colored-diff' is not supported on this machine. "
2041 "To get colored diff output, install GNU diffutils version "
2042 "3.4 or higher.");
2043 } else {
2044 // Colored diff is disabled, clear the diff string and try unified
2045 // diff with "diff -u".
2046
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 dynstr_set(&ds_diff, "");
2047
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 exit_code = run_tool(diff_name, &ds_diff, "-u", filename1, filename2,
2048 "2>&1", NULL);
2049
2050
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (exit_code > 1) {
2051 // Clear the diff string and fallback to context diff with "diff -c"
2052 dynstr_set(&ds_diff, "");
2053 exit_code = run_tool(diff_name, &ds_diff, "-c", filename1, filename2,
2054 "2>&1", NULL);
2055
2056 if (exit_code > 1) {
2057 // Clear the diff string and fallback to simple diff with "diff"
2058 dynstr_set(&ds_diff, "");
2059 exit_code =
2060 run_tool(diff_name, &ds_diff, filename1, filename2, "2>&1", NULL);
2061 if (exit_code > 1) diff_name = nullptr;
2062 }
2063
3/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
15 } else if (exit_code == 1 && opt_hypergraph &&
2064 is_diff_clean_except_hypergraph(&ds_diff)) {
2065 dynstr_free(&ds_diff);
2066 return true;
2067 }
2068 }
2069 }
2070
2071
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (!diff_name) {
2072 // Fallback to dump both files to result file and inform
2073 // about installing "diff".
2074 dynstr_append(&ds_diff, "\n");
2075 dynstr_append(
2076 &ds_diff,
2077 "\n"
2078 "The two files differ but it was not possible to execute 'diff' in\n"
2079 "order to show only the difference. Instead the whole content of the\n"
2080 "two files was shown for you to diff manually.\n\n"
2081 "To get a better report you should install 'diff' on your system, "
2082 "which you\n"
2083 "for example can get from "
2084 "http://www.gnu.org/software/diffutils/diffutils.html\n"
2085 #ifdef _WIN32
2086 "or http://gnuwin32.sourceforge.net/packages/diffutils.htm\n"
2087 #endif
2088 "\n");
2089
2090 dynstr_append(&ds_diff, " --- ");
2091 dynstr_append(&ds_diff, filename1);
2092 dynstr_append(&ds_diff, " >>>\n");
2093 cat_file(&ds_diff, filename1);
2094 dynstr_append(&ds_diff, "<<<\n --- ");
2095 dynstr_append(&ds_diff, filename1);
2096 dynstr_append(&ds_diff, " >>>\n");
2097 cat_file(&ds_diff, filename2);
2098 dynstr_append(&ds_diff, "<<<<\n");
2099 }
2100
2101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (ds)
2102 // Add the diff to output
2103 dynstr_append_mem(ds, ds_diff.str, ds_diff.length);
2104 else
2105 // Print diff directly to stderr
2106
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 fprintf(stderr, "%s\n", ds_diff.str);
2107
2108
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 dynstr_free(&ds_diff);
2109 15 return false;
2110 }
2111
2112 enum compare_files_result_enum {
2113 RESULT_OK = 0,
2114 RESULT_CONTENT_MISMATCH = 1,
2115 RESULT_LENGTH_MISMATCH = 2
2116 };
2117
2118 /*
2119 Compare two files, given a fd to the first file and
2120 name of the second file
2121
2122 SYNOPSIS
2123 compare_files2
2124 fd - Open file descriptor of the first file
2125 filename2 - Name of second file
2126
2127 RETURN VALUES
2128 According to the values in "compare_files_result_enum"
2129
2130 */
2131
2132 41416 static int compare_files2(File fd, const char *filename2) {
2133 41416 int error = RESULT_OK;
2134 File fd2;
2135 size_t len, len2;
2136 char buff[512], buff2[512];
2137
2138
2/4
✓ Branch 0 taken 41416 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 41416 times.
41416 if ((fd2 = my_open(filename2, O_RDONLY, MYF(0))) < 0) {
2139 my_close(fd, MYF(0));
2140 die("Failed to open second file: '%s'", filename2);
2141 }
2142
3/4
✓ Branch 0 taken 14675283 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14633886 times.
✓ Branch 3 taken 41397 times.
14675283 while ((len = my_read(fd, (uchar *)&buff, sizeof(buff), MYF(0))) > 0) {
2143
3/4
✓ Branch 0 taken 14633886 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 14633881 times.
14633886 if ((len2 = my_read(fd2, (uchar *)&buff2, sizeof(buff2), MYF(0))) < len) {
2144 /* File 2 was smaller */
2145 5 error = RESULT_LENGTH_MISMATCH;
2146 5 break;
2147 }
2148
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14633880 times.
14633881 if (len2 > len) {
2149 /* File 1 was smaller */
2150 1 error = RESULT_LENGTH_MISMATCH;
2151 1 break;
2152 }
2153
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 14633867 times.
14633880 if ((memcmp(buff, buff2, len))) {
2154 /* Content of this part differed */
2155 13 error = RESULT_CONTENT_MISMATCH;
2156 13 break;
2157 }
2158 }
2159
5/8
✓ Branch 0 taken 41397 times.
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 41397 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 41397 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 41416 times.
41416 if (!error && my_read(fd2, (uchar *)&buff2, sizeof(buff2), MYF(0)) > 0) {
2160 /* File 1 was smaller */
2161 error = RESULT_LENGTH_MISMATCH;
2162 }
2163
2164
1/2
✓ Branch 0 taken 41416 times.
✗ Branch 1 not taken.
41416 my_close(fd2, MYF(0));
2165
2166 41416 return error;
2167 }
2168
2169 /*
2170 Compare two files, given their filenames
2171
2172 SYNOPSIS
2173 compare_files
2174 filename1 - Name of first file
2175 filename2 - Name of second file
2176
2177 RETURN VALUES
2178 See 'compare_files2'
2179
2180 */
2181
2182 41416 static int compare_files(const char *filename1, const char *filename2) {
2183 File fd;
2184 int error;
2185
2186
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41416 times.
41416 if ((fd = my_open(filename1, O_RDONLY, MYF(0))) < 0)
2187 die("Failed to open first file: '%s'", filename1);
2188
2189 41416 error = compare_files2(fd, filename2);
2190
2191 41416 my_close(fd, MYF(0));
2192
2193 41416 return error;
2194 }
2195
2196 /*
2197 Check the content of log against result file
2198
2199 SYNOPSIS
2200 check_result
2201
2202 RETURN VALUES
2203 error - the function will not return
2204
2205 */
2206
2207 24534 static void check_result() {
2208 24534 const char *mess = "Result content mismatch\n";
2209
2210
1/2
✓ Branch 0 taken 24534 times.
✗ Branch 1 not taken.
24534 DBUG_TRACE;
2211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24534 times.
24534 assert(result_file_name);
2212
3/8
✓ Branch 0 taken 24534 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24534 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 24534 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
24534 DBUG_PRINT("enter", ("result_file_name: %s", result_file_name));
2213
2214 /*
2215 Removing the unnecessary warning messages generated
2216 on GCOV platform.
2217 */
2218 #ifdef HAVE_GCOV
2219 char cmd[FN_REFLEN];
2220 24534 strcpy(cmd, "sed -i '/gcda:Merge mismatch for function/d' ");
2221 24534 std::strcat(cmd, log_file.file_name());
2222
1/2
✓ Branch 0 taken 24534 times.
✗ Branch 1 not taken.
24534 system(cmd);
2223 #endif
2224
2225
4/6
✓ Branch 0 taken 24534 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24519 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
24534 switch (compare_files(log_file.file_name(), result_file_name)) {
2226 24519 case RESULT_OK:
2227 24519 break; /* ok */
2228 4 case RESULT_LENGTH_MISMATCH:
2229 4 mess = "Result length mismatch\n";
2230 [[fallthrough]];
2231 15 case RESULT_CONTENT_MISMATCH: {
2232 /*
2233 Result mismatched, dump results to .reject file
2234 and then show the diff
2235 */
2236 char reject_file[FN_REFLEN];
2237 size_t reject_length;
2238
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 dirname_part(reject_file, result_file_name, &reject_length);
2239
2240 /* Put reject file in opt_logdir */
2241
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 fn_format(reject_file, result_file_name, opt_logdir, ".reject",
2242 MY_REPLACE_DIR | MY_REPLACE_EXT);
2243
2244
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 if (my_copy(log_file.file_name(), reject_file, MYF(0)) != 0)
2245 die("Failed to copy '%s' to '%s', errno: %d", log_file.file_name(),
2246 reject_file, errno);
2247
2248
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 bool ignored_diff = show_diff(nullptr, result_file_name, reject_file);
2249
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (ignored_diff) {
2250 abort_not_supported_test(
2251 "Hypergraph optimizer did not support all queries.");
2252 }
2253 15 die("%s", mess);
2254 break;
2255 }
2256 default: /* impossible */
2257 die("Unknown error code from dyn_string_cmp()");
2258 }
2259 24519 }
2260
2261 /*
2262 Remove surrounding chars from string
2263
2264 Return 1 if first character is found but not last
2265 */
2266 860880 static int strip_surrounding(char *str, char c1, char c2) {
2267 860880 char *ptr = str;
2268
2269 /* Check if the first non space character is c1 */
2270
4/4
✓ Branch 0 taken 879890 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 19012 times.
✓ Branch 3 taken 860878 times.
879892 while (*ptr && my_isspace(charset_info, *ptr)) ptr++;
2271
2/2
✓ Branch 0 taken 435096 times.
✓ Branch 1 taken 425784 times.
860880 if (*ptr == c1) {
2272 /* Replace it with a space */
2273 435096 *ptr = ' ';
2274
2275 /* Last non space character should be c2 */
2276 435096 ptr = strend(str) - 1;
2277
3/4
✓ Branch 0 taken 435099 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 435096 times.
435099 while (*ptr && my_isspace(charset_info, *ptr)) ptr--;
2278
2/2
✓ Branch 0 taken 435095 times.
✓ Branch 1 taken 1 times.
435096 if (*ptr == c2) {
2279 /* Replace it with \0 */
2280 435095 *ptr = 0;
2281 } else {
2282 /* Mismatch detected */
2283 1 return 1;
2284 }
2285 }
2286 860879 return 0;
2287 }
2288
2289 454007 static void strip_parentheses(struct st_command *command) {
2290
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 454006 times.
454007 if (strip_surrounding(command->first_argument, '(', ')'))
2291 1 die("%.*s - argument list started with '%c' must be ended with '%c'",
2292 1 static_cast<int>(command->first_word_len), command->query, '(', ')');
2293 454006 }
2294
2295 3059919 void var_free::operator()(VAR *var) const {
2296 3059919 my_free(var->str_val);
2297
1/2
✓ Branch 0 taken 3059919 times.
✗ Branch 1 not taken.
3059919 if (var->alloced) my_free(var);
2298 3059919 }
2299
2300 110742876 static void var_check_int(VAR *v) {
2301 char *endptr;
2302 110742876 char *str = v->str_val;
2303
2304 /* Initially assume not a number */
2305 110742876 v->int_val = 0;
2306 110742876 v->is_int = false;
2307 110742876 v->int_dirty = false;
2308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 110742876 times.
110742876 if (!str) return;
2309
2310 110742876 v->int_val = (int)strtol(str, &endptr, 10);
2311 /* It is an int if strtol consumed something up to end/space/tab */
2312
8/8
✓ Branch 0 taken 31233975 times.
✓ Branch 1 taken 79508901 times.
✓ Branch 2 taken 400228 times.
✓ Branch 3 taken 30833747 times.
✓ Branch 4 taken 265837 times.
✓ Branch 5 taken 134391 times.
✓ Branch 6 taken 14 times.
✓ Branch 7 taken 265823 times.
110742876 if (endptr > str && (!*endptr || *endptr == ' ' || *endptr == '\t'))
2313 30968152 v->is_int = true;
2314 }
2315
2316 48192407 VAR *var_init(VAR *v, const char *name, size_t name_len, const char *val,
2317 size_t val_len) {
2318 size_t val_alloc_len;
2319 VAR *tmp_var;
2320
3/4
✓ Branch 0 taken 45132473 times.
✓ Branch 1 taken 3059934 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 45132473 times.
48192407 if (!name_len && name) name_len = std::strlen(name);
2321
4/4
✓ Branch 0 taken 48030764 times.
✓ Branch 1 taken 161643 times.
✓ Branch 2 taken 2898291 times.
✓ Branch 3 taken 45132473 times.
48192407 if (!val_len && val) val_len = std::strlen(val);
2322
2/2
✓ Branch 0 taken 45132473 times.
✓ Branch 1 taken 3059934 times.
48192407 if (!val) val_len = 0;
2323 48192407 val_alloc_len = val_len + 16; /* room to grow */
2324
4/6
✓ Branch 0 taken 3059934 times.
✓ Branch 1 taken 45132473 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3059934 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 48192407 times.
48192407 if (!(tmp_var = v) && !(tmp_var = (VAR *)my_malloc(
2325 PSI_NOT_INSTRUMENTED,
2326 sizeof(*tmp_var) + name_len + 2, MYF(MY_WME))))
2327 die("Out of memory");
2328
2329
2/2
✓ Branch 0 taken 3059934 times.
✓ Branch 1 taken 45132473 times.
48192407 if (name != nullptr) {
2330 3059934 tmp_var->name = reinterpret_cast<char *>(tmp_var) + sizeof(*tmp_var);
2331 3059934 memcpy(tmp_var->name, name, name_len);
2332 3059934 tmp_var->name[name_len] = 0;
2333 } else
2334 45132473 tmp_var->name = nullptr;
2335
2336 48192407 tmp_var->alloced = (v == nullptr);
2337
2338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48192407 times.
48192407 if (!(tmp_var->str_val = (char *)my_malloc(PSI_NOT_INSTRUMENTED,
2339 val_alloc_len + 1, MYF(MY_WME))))
2340 die("Out of memory");
2341
2342
2/2
✓ Branch 0 taken 3059934 times.
✓ Branch 1 taken 45132473 times.
48192407 if (val) memcpy(tmp_var->str_val, val, val_len);
2343 48192407 tmp_var->str_val[val_len] = 0;
2344
2345 48192407 var_check_int(tmp_var);
2346 48192407 tmp_var->name_len = name_len;
2347 48192407 tmp_var->str_val_len = val_len;
2348 48192407 tmp_var->alloced_len = val_alloc_len;
2349 48192407 return tmp_var;
2350 }
2351
2352 701291 VAR *var_from_env(const char *name, const char *def_val) {
2353 const char *tmp;
2354 VAR *v;
2355
2/2
✓ Branch 0 taken 539034 times.
✓ Branch 1 taken 162257 times.
701291 if (!(tmp = getenv(name))) tmp = def_val;
2356
2357 701291 v = var_init(nullptr, name, std::strlen(name), tmp, std::strlen(tmp));
2358
1/2
✓ Branch 0 taken 701291 times.
✗ Branch 1 not taken.
701291 var_hash->emplace(name, std::unique_ptr<VAR, var_free>(v));
2359 701291 return v;
2360 }
2361
2362 87844744 VAR *var_get(const char *var_name, const char **var_name_end, bool raw,
2363 bool ignore_not_existing) {
2364 int digit;
2365 VAR *v;
2366
1/2
✓ Branch 0 taken 87844744 times.
✗ Branch 1 not taken.
87844744 DBUG_TRACE;
2367
3/8
✓ Branch 0 taken 87844744 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 87844744 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 87844744 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
87844744 DBUG_PRINT("enter", ("var_name: %s", var_name));
2368
2369
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 87844744 times.
87844744 if (*var_name != '$') goto err;
2370 87844744 digit = *++var_name - '0';
2371
3/4
✓ Branch 0 taken 87844744 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 85345677 times.
✓ Branch 3 taken 2499067 times.
87844744 if (digit < 0 || digit >= 10) {
2372 85345677 const char *save_var_name = var_name, *end;
2373 uint length;
2374
2/2
✓ Branch 0 taken 85344714 times.
✓ Branch 1 taken 963 times.
85345677 end = (var_name_end) ? *var_name_end : nullptr;
2375
5/6
✓ Branch 0 taken 201521670 times.
✓ Branch 1 taken 852263949 times.
✓ Branch 2 taken 116175993 times.
✓ Branch 3 taken 85345677 times.
✓ Branch 4 taken 968439942 times.
✗ Branch 5 not taken.
1053785619 while (my_isvar(charset_info, *var_name) && var_name != end) var_name++;
2376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85345677 times.
85345677 if (var_name == save_var_name) {
2377 if (ignore_not_existing) return nullptr;
2378 die("Empty variable");
2379 }
2380 85345677 length = (uint)(var_name - save_var_name);
2381
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85345677 times.
85345677 if (length >= MAX_VAR_NAME_LENGTH)
2382 die("Too long variable name: %s", save_var_name);
2383
2384
4/6
✓ Branch 0 taken 85345677 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 85345677 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 701291 times.
✓ Branch 5 taken 84644386 times.
85345677 if (!(v = find_or_nullptr(*var_hash, std::string(save_var_name, length)))) {
2385 char buff[MAX_VAR_NAME_LENGTH + 1];
2386
1/2
✓ Branch 0 taken 701291 times.
✗ Branch 1 not taken.
701291 strmake(buff, save_var_name, length);
2387
1/2
✓ Branch 0 taken 701291 times.
✗ Branch 1 not taken.
701291 v = var_from_env(buff, "");
2388 }
2389 85345677 var_name--; /* Point at last character */
2390 85345677 } else
2391 2499067 v = var_reg + digit;
2392
2393
4/4
✓ Branch 0 taken 78099058 times.
✓ Branch 1 taken 9745686 times.
✓ Branch 2 taken 9536205 times.
✓ Branch 3 taken 68562853 times.
87844744 if (!raw && v->int_dirty) {
2394 9536205 sprintf(v->str_val, "%d", v->int_val);
2395 9536205 v->int_dirty = false;
2396 9536205 v->str_val_len = std::strlen(v->str_val);
2397 }
2398
2/2
✓ Branch 0 taken 87843781 times.
✓ Branch 1 taken 963 times.
87844744 if (var_name_end) *var_name_end = var_name;
2399 87844744 return v;
2400 err:
2401 if (var_name_end) *var_name_end = nullptr;
2402 die("Unsupported variable name: %s", var_name);
2403 return nullptr;
2404 87844744 }
2405
2406 53007562 static VAR *var_obtain(const char *name, int len) {
2407
2/4
✓ Branch 0 taken 53007562 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 53007562 times.
✗ Branch 3 not taken.
53007562 VAR *v = find_or_nullptr(*var_hash, std::string(name, len));
2408
2/2
✓ Branch 0 taken 2358643 times.
✓ Branch 1 taken 50648919 times.
53007562 if (v == nullptr) {
2409 2358643 v = var_init(nullptr, name, len, "", 0);
2410
2/4
✓ Branch 0 taken 2358643 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2358643 times.
✗ Branch 3 not taken.
2358643 var_hash->emplace(std::string(name, len),
2411 4717286 std::unique_ptr<VAR, var_free>(v));
2412 }
2413 53007562 return v;
2414 }
2415
2416 /*
2417 - if variable starts with a $ it is regarded as a local test variable
2418 - if not it is treated as a environment variable, and the corresponding
2419 environment variable will be updated
2420 */
2421
2422 53012257 void var_set(const char *var_name, const char *var_name_end,
2423 const char *var_val, const char *var_val_end) {
2424 53012257 int digit, env_var = 0;
2425 VAR *v;
2426
1/2
✓ Branch 0 taken 53012257 times.
✗ Branch 1 not taken.
53012257 DBUG_TRACE;
2427
3/8
✓ Branch 0 taken 53012257 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 53012257 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 53012257 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
53012257 DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)",
2428 (int)(var_name_end - var_name), var_name,
2429 (int)(var_val_end - var_val), var_val,
2430 (int)(var_val_end - var_val)));
2431
2432
2/2
✓ Branch 0 taken 1200186 times.
✓ Branch 1 taken 51812071 times.
53012257 if (*var_name != '$')
2433 1200186 env_var = 1;
2434 else
2435 51812071 var_name++;
2436
2437 53012257 digit = *var_name - '0';
2438
4/4
✓ Branch 0 taken 4697 times.
✓ Branch 1 taken 53007560 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 4695 times.
53012257 if (!(digit < 10 && digit >= 0)) {
2439
1/2
✓ Branch 0 taken 53007562 times.
✗ Branch 1 not taken.
53007562 v = var_obtain(var_name, (uint)(var_name_end - var_name));
2440 } else
2441 4695 v = var_reg + digit;
2442
2443
1/2
✓ Branch 0 taken 53012230 times.
✗ Branch 1 not taken.
53012257 eval_expr(v, var_val, (const char **)&var_val_end);
2444
2445
2/2
✓ Branch 0 taken 1200186 times.
✓ Branch 1 taken 51812044 times.
53012230 if (env_var) {
2446
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1200186 times.
1200186 if (v->int_dirty) {
2447 sprintf(v->str_val, "%d", v->int_val);
2448 v->int_dirty = false;
2449 v->str_val_len = std::strlen(v->str_val);
2450 }
2451 /* setenv() expects \0-terminated strings */
2452
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1200186 times.
1200186 assert(v->name[v->name_len] == 0);
2453 1200186 setenv(v->name, v->str_val, 1);
2454 }
2455 53012230 }
2456
2457 27178748 static void var_set_string(const char *name, const char *value) {
2458 27178748 var_set(name, name + std::strlen(name), value, value + std::strlen(value));
2459 27178748 }
2460
2461 14934641 static void var_set_int(const char *name, int value) {
2462 char buf[21];
2463 14934641 snprintf(buf, sizeof(buf), "%d", value);
2464
1/2
✓ Branch 0 taken 14934641 times.
✗ Branch 1 not taken.
14934641 var_set_string(name, buf);
2465 14934641 }
2466
2467 /*
2468 Store an integer (typically the returncode of the last SQL)
2469 statement in the mysqltest builtin variable $mysql_errno
2470 */
2471
2472 10509810 static void var_set_errno(int sql_errno) {
2473 10509810 var_set_int("$mysql_errno", sql_errno);
2474 10509810 var_set_string("$mysql_errname", get_errname_from_code(sql_errno));
2475 10509810 }
2476
2477 /// Variable '$DISABLED_WARNINGS_LIST' contains comma separated list
2478 /// of disabled warnings and variable '$ENABLED_WARNINGS_LIST' contains
2479 /// comma separated list of enabled warnings.
2480 ///
2481 /// Update the value of these two variables with the latest list of
2482 /// disabled and enabled warnings. The value of these variables will be
2483 /// empty if there are no disabled or enabled warnings.
2484 ///
2485 /// These variables will always contain the latest list of disabled
2486 /// and enabled warnings, and can be referenced inside a test or inside
2487 /// a test utility file to access the current list of disabled or
2488 /// enabled warnings.
2489 85475 static void update_disabled_enabled_warnings_list_var() {
2490 // Update '$DISABLED_WARNINGS_LIST' variable
2491
1/2
✓ Branch 0 taken 85475 times.
✗ Branch 1 not taken.
85475 std::string disabled_warning_list = disabled_warnings->warnings_list();
2492
1/2
✓ Branch 0 taken 85475 times.
✗ Branch 1 not taken.
85475 var_set_string("DISABLED_WARNINGS_LIST", disabled_warning_list.c_str());
2493
2494 // Update '$ENABLED_WARNINGS_LIST' variable
2495
1/2
✓ Branch 0 taken 85475 times.
✗ Branch 1 not taken.
85475 std::string enabled_warning_list = enabled_warnings->warnings_list();
2496
1/2
✓ Branch 0 taken 85475 times.
✗ Branch 1 not taken.
85475 var_set_string("ENABLED_WARNINGS_LIST", enabled_warning_list.c_str());
2497 85475 }
2498
2499 /// Set a property value to either 0 or 1 for a disable_X or a enable_X
2500 /// command, and the new value set will be applicable for next statement
2501 /// only. After that, property value will be reset back to the old value.
2502 ///
2503 /// @param property Enum value representing a Property
2504 /// @param value Value for the property, either 0 or 1
2505 15926 static void set_once_property(enum_prop property, bool value) {
2506 15926 Property &prop = prop_list[property];
2507 15926 prop.set = true;
2508 15926 prop.old = *prop.var;
2509 15926 *prop.var = value;
2510 15926 var_set_int(prop.env_name, (value != prop.reverse));
2511 15926 once_property = true;
2512 15926 }
2513
2514 /// Set a property value to either 0 or 1 for a disable_X or a enable_X
2515 /// command.
2516 ///
2517 /// @param command Pointer to the st_command structure which holds the
2518 /// arguments and information for the command.
2519 /// @param property Enum value representing a Property
2520 /// @param value Value for the property, either 0 or 1
2521 2346042 static void set_property(st_command *command, enum_prop property, bool value) {
2522 2346042 char *arg = command->first_argument;
2523
2524 // If "ONCE" argument is specified, the new value for the property is
2525 // set for next statement only. After that, property value will be
2526 // reset back to the old value.
2527
1/2
✓ Branch 0 taken 2346042 times.
✗ Branch 1 not taken.
2346042 if (arg) {
2528 // "ONCE" is the second argument to 'disable_warnings/enable_warnings'
2529 // command.
2530
2/2
✓ Branch 0 taken 2304302 times.
✓ Branch 1 taken 41740 times.
2346042 if (((command->type == Q_DISABLE_WARNINGS ||
2531
2/2
✓ Branch 0 taken 34063 times.
✓ Branch 1 taken 2270239 times.
2304302 command->type == Q_ENABLE_WARNINGS) &&
2532
2/2
✓ Branch 0 taken 74546 times.
✓ Branch 1 taken 1257 times.
75803 std::strstr(arg, "ONCE") != nullptr) ||
2533
2/2
✓ Branch 0 taken 14662 times.
✓ Branch 1 taken 2330123 times.
2344785 !std::strcmp(arg, "ONCE")) {
2534 15919 command->last_argument = arg + std::strlen(arg);
2535 15919 set_once_property(property, value);
2536 15919 return;
2537 }
2538 }
2539
2540 2330123 Property &prop = prop_list[property];
2541 2330123 prop.set = false;
2542 2330123 *prop.var = value;
2543 2330123 var_set_int(prop.env_name, (value != prop.reverse));
2544 }
2545
2546 /// Reset property value to the old value for all properties which are
2547 /// set for the next statement only, i.e properties specified using
2548 /// keyword "ONCE" argument.
2549 36181898 void revert_properties() {
2550
2/2
✓ Branch 0 taken 36172245 times.
✓ Branch 1 taken 9653 times.
36181898 if (!once_property) return;
2551
2552
2/2
✓ Branch 0 taken 96530 times.
✓ Branch 1 taken 9653 times.
106183 for (std::size_t i = 0; i < P_MAX; i++) {
2553 96530 Property &prop = prop_list[i];
2554
2/2
✓ Branch 0 taken 15926 times.
✓ Branch 1 taken 80604 times.
96530 if (prop.set) {
2555 15926 *prop.var = prop.old;
2556 15926 prop.set = false;
2557 15926 var_set_int(prop.env_name, (prop.old != prop.reverse));
2558 }
2559 }
2560
2561 // Remove warnings which are disabled or enabled for the next
2562 // statement only.
2563 9653 disabled_warnings->update_list();
2564 9653 enabled_warnings->update_list();
2565
2566 // Update $DISABLED_WARNINGS_LIST and $ENABLED_WARNINGS_LIST
2567 // variable value.
2568 9653 update_disabled_enabled_warnings_list_var();
2569
2570 9653 once_property = false;
2571 }
2572
2573 /*
2574 Set variable from the result of a query
2575
2576 SYNOPSIS
2577 var_query_set()
2578 var variable to set from query
2579 query start of query string to execute
2580 query_end end of the query string to execute
2581
2582
2583 DESCRIPTION
2584 let @<var_name> = `<query>`
2585
2586 Execute the query and assign the first row of result to var as
2587 a tab separated strings
2588
2589 Also assign each column of the result set to
2590 variable "$<var_name>_<column_name>"
2591 Thus the tab separated output can be read from $<var_name> and
2592 and each individual column can be read as $<var_name>_<col_name>
2593
2594 */
2595
2596 8281869 static void var_query_set(VAR *var, const char *query, const char **query_end) {
2597 8281869 const char *end =
2598
2/4
✓ Branch 0 taken 8281869 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8281869 times.
✗ Branch 3 not taken.
8281869 (query_end && *query_end) ? *query_end : query + std::strlen(query);
2599 8281869 MYSQL_RES *res = nullptr;
2600 MYSQL_ROW row;
2601 8281869 MYSQL *mysql = &cur_con->mysql;
2602 DYNAMIC_STRING ds_query;
2603
1/2
✓ Branch 0 taken 8281869 times.
✗ Branch 1 not taken.
8281869 DBUG_TRACE;
2604
2605 /* Only white space or ) allowed past ending ` */
2606
3/4
✓ Branch 0 taken 16573649 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8291783 times.
✓ Branch 3 taken 8281866 times.
16573649 while (end > query && *end != '`') {
2607
8/10
✓ Branch 0 taken 271107 times.
✓ Branch 1 taken 8020676 times.
✓ Branch 2 taken 261196 times.
✓ Branch 3 taken 9911 times.
✓ Branch 4 taken 261196 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 261196 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 261193 times.
8291783 if (*end && (*end != ' ' && *end != '\t' && *end != '\n' && *end != ')'))
2608 3 die("Spurious text after `query` expression");
2609 8291780 --end;
2610 }
2611
2612
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8281866 times.
8281866 if (query == end) die("Syntax error in query, missing '`'");
2613 8281866 ++query;
2614
2615 /* Eval the query, thus replacing all environment variables */
2616
1/2
✓ Branch 0 taken 8281866 times.
✗ Branch 1 not taken.
8281866 init_dynamic_string(&ds_query, nullptr, (end - query) + 32);
2617
1/2
✓ Branch 0 taken 8281866 times.
✗ Branch 1 not taken.
8281866 do_eval(&ds_query, query, end, false);
2618
2619
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8281862 times.
8281866 if (mysql_real_query_wrapper(mysql, ds_query.str,
2620
1/2
✓ Branch 0 taken 8281866 times.
✗ Branch 1 not taken.
8281866 static_cast<ulong>(ds_query.length))) {
2621
4/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
4 handle_error(curr_command, mysql_errno(mysql), mysql_error(mysql),
2622 mysql_sqlstate(mysql), &ds_res);
2623 /* If error was acceptable, return empty string */
2624
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 dynstr_free(&ds_query);
2625
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 eval_expr(var, "", nullptr);
2626 3 return;
2627 }
2628
2629
2/4
✓ Branch 0 taken 8281862 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8281862 times.
8281862 if (!(res = mysql_store_result_wrapper(mysql)))
2630 die("Query '%s' didn't return a result set", ds_query.str);
2631
1/2
✓ Branch 0 taken 8281862 times.
✗ Branch 1 not taken.
8281862 dynstr_free(&ds_query);
2632
2633
7/8
✓ Branch 0 taken 8281862 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8235514 times.
✓ Branch 3 taken 46348 times.
✓ Branch 4 taken 8235050 times.
✓ Branch 5 taken 464 times.
✓ Branch 6 taken 8235050 times.
✓ Branch 7 taken 46812 times.
8281862 if ((row = mysql_fetch_row_wrapper(res)) && row[0]) {
2634 /*
2635 Concatenate all fields in the first row with tab in between
2636 and assign that string to the $variable
2637 */
2638 DYNAMIC_STRING result;
2639 uint i;
2640 ulong *lengths;
2641
2642
1/2
✓ Branch 0 taken 8235050 times.
✗ Branch 1 not taken.
8235050 init_dynamic_string(&result, "", 512);
2643
1/2
✓ Branch 0 taken 8235050 times.
✗ Branch 1 not taken.
8235050 lengths = mysql_fetch_lengths(res);
2644
3/4
✓ Branch 0 taken 16472128 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8237078 times.
✓ Branch 3 taken 8235050 times.
16472128 for (i = 0; i < mysql_num_fields(res); i++) {
2645
2/2
✓ Branch 0 taken 8237072 times.
✓ Branch 1 taken 6 times.
8237078 if (row[i]) {
2646 /* Add column to tab separated string */
2647 8237072 char *val = row[i];
2648 8237072 size_t len = lengths[i];
2649
2650
2/2
✓ Branch 0 taken 1135 times.
✓ Branch 1 taken 8235937 times.
8237072 if (glob_replace_regex) {
2651 1135 size_t orig_len = len;
2652 // Regex replace
2653
3/4
✓ Branch 0 taken 1135 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 114 times.
✓ Branch 3 taken 1021 times.
1135 if (!multi_reg_replace(glob_replace_regex, (char *)val, &len)) {
2654 114 val = glob_replace_regex->buf;
2655 } else {
2656 1021 len = orig_len;
2657 }
2658 }
2659 DYNAMIC_STRING ds_temp;
2660
1/2
✓ Branch 0 taken 8237072 times.
✗ Branch 1 not taken.
8237072 init_dynamic_string(&ds_temp, "", 512);
2661
2662 /* Store result from replace_result in ds_temp */
2663
2/2
✓ Branch 0 taken 153 times.
✓ Branch 1 taken 8236919 times.
8237072 if (glob_replace)
2664
1/2
✓ Branch 0 taken 153 times.
✗ Branch 1 not taken.
153 replace_strings_append(glob_replace, &ds_temp, val, len);
2665
2666 /*
2667 Call the replace_numeric_round function with the specified
2668 precision. It may be used along with replace_result, so use the
2669 output from replace_result as the input for replace_numeric_round.
2670 */
2671
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 8237069 times.
8237072 if (glob_replace_numeric_round >= 0) {
2672 /* Copy the result from replace_result if it was used, into buffer */
2673
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (ds_temp.length > 0) {
2674 char buffer[512];
2675 strcpy(buffer, ds_temp.str);
2676 dynstr_free(&ds_temp);
2677 init_dynamic_string(&ds_temp, "", 512);
2678 replace_numeric_round_append(glob_replace_numeric_round, &ds_temp,
2679 buffer, std::strlen(buffer));
2680 } else
2681
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 replace_numeric_round_append(glob_replace_numeric_round, &ds_temp,
2682 val, len);
2683 }
2684
2685
4/4
✓ Branch 0 taken 8236919 times.
✓ Branch 1 taken 153 times.
✓ Branch 2 taken 8236916 times.
✓ Branch 3 taken 3 times.
8237072 if (!glob_replace && glob_replace_numeric_round < 0)
2686
1/2
✓ Branch 0 taken 8236916 times.
✗ Branch 1 not taken.
8236916 dynstr_append_mem(&result, val, len);
2687 else
2688
1/2
✓ Branch 0 taken 156 times.
✗ Branch 1 not taken.
156 dynstr_append_mem(&result, ds_temp.str, std::strlen(ds_temp.str));
2689
1/2
✓ Branch 0 taken 8237072 times.
✗ Branch 1 not taken.
8237072 dynstr_free(&ds_temp);
2690 }
2691
1/2
✓ Branch 0 taken 8237078 times.
✗ Branch 1 not taken.
8237078 dynstr_append_mem(&result, "\t", 1);
2692 }
2693 8235050 end = result.str + result.length - 1;
2694 /* Evaluation should not recurse via backtick */
2695
1/2
✓ Branch 0 taken 8235050 times.
✗ Branch 1 not taken.
8235050 eval_expr(var, result.str, &end, false, false);
2696
1/2
✓ Branch 0 taken 8235050 times.
✗ Branch 1 not taken.
8235050 dynstr_free(&result);
2697 } else
2698
1/2
✓ Branch 0 taken 46812 times.
✗ Branch 1 not taken.
46812 eval_expr(var, "", nullptr);
2699
2700
1/2
✓ Branch 0 taken 8281862 times.
✗ Branch 1 not taken.
8281862 mysql_free_result_wrapper(res);
2701
2/2
✓ Branch 0 taken 8281862 times.
✓ Branch 1 taken 3 times.
8281865 }
2702
2703 static void set_result_format_version(ulong new_version) {
2704 switch (new_version) {
2705 case 1:
2706 /* The first format */
2707 break;
2708 case 2:
2709 /* New format that also writes comments and empty lines
2710 from test file to result */
2711 break;
2712 default:
2713 die("Version format %lu has not yet been implemented", new_version);
2714 break;
2715 }
2716 opt_result_format_version = new_version;
2717 }
2718
2719 /*
2720 Set the result format version to use when generating
2721 the .result file
2722 */
2723
2724 static void do_result_format_version(struct st_command *command) {
2725 long version;
2726 static DYNAMIC_STRING ds_version;
2727 const struct command_arg result_format_args[] = {
2728 {"version", ARG_STRING, true, &ds_version, "Version to use"}};
2729
2730 DBUG_TRACE;
2731
2732 check_command_args(command, command->first_argument, result_format_args,
2733 sizeof(result_format_args) / sizeof(struct command_arg),
2734 ',');
2735
2736 /* Convert version number to int */
2737 if (!str2int(ds_version.str, 10, (long)0, (long)INT_MAX, &version))
2738 die("Invalid version number: '%s'", ds_version.str);
2739
2740 set_result_format_version(version);
2741
2742 dynstr_append(&ds_res, "result_format: ");
2743 dynstr_append_mem(&ds_res, ds_version.str, ds_version.length);
2744 dynstr_append(&ds_res, "\n");
2745 dynstr_free(&ds_version);
2746 }
2747
2748 /// Convert between error numbers and error names/strings.
2749 ///
2750 /// @code
2751 /// let $var = convert_error(ER_UNKNOWN_ERROR);
2752 /// let $var = convert_error(1234);
2753 /// @endcode
2754 ///
2755 /// The variable '$var' will be populated with error number if the
2756 /// argument is string. The variable var will be populated with error
2757 /// string if the argument is number.
2758 ///
2759 /// @param command Pointer to the st_command structure which holds the
2760 /// arguments and information for the command.
2761 /// @param var Pointer to VAR object containing a variable
2762 /// information.
2763 960 static void var_set_convert_error(struct st_command *command, VAR *var) {
2764 // The command->query contains the statement convert_error(1234)
2765 960 char *first = std::strchr(command->query, '(') + 1;
2766 960 char *last = std::strchr(command->query, ')');
2767
2768 // Denoting an empty string
2769
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 958 times.
960 if (last == first) {
2770
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 eval_expr(var, "0", nullptr);
2771 2 return;
2772 }
2773
2774 // If the string is an error string , it starts with 'E' as is the norm
2775
2/2
✓ Branch 0 taken 951 times.
✓ Branch 1 taken 7 times.
958 if (*first == 'E') {
2776
1/2
✓ Branch 0 taken 951 times.
✗ Branch 1 not taken.
951 std::string error_name(first, int(last - first));
2777
2/4
✓ Branch 0 taken 951 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 951 times.
✗ Branch 3 not taken.
951 int error = get_errcode_from_name(error_name);
2778
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 951 times.
951 if (error == -1) die("Unknown SQL error name '%s'.", error_name.c_str());
2779 char str[100];
2780 951 std::sprintf(str, "%d", error);
2781
1/2
✓ Branch 0 taken 951 times.
✗ Branch 1 not taken.
951 eval_expr(var, str, nullptr);
2782
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
958 } else if (my_isdigit(charset_info, *first)) {
2783 // Error number argument
2784 6 long int err = std::strtol(first, &last, 0);
2785
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 const char *err_name = get_errname_from_code(err);
2786
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 eval_expr(var, err_name, nullptr);
2787 } else {
2788 1 die("Invalid error in input");
2789 }
2790 }
2791
2792 /**
2793 Allocate memory for the buffer holding the string value of a variable.
2794
2795 @param var The variable
2796 @param length The number of string characters, not counting the \0.
2797 */
2798 42249 static void alloc_var(VAR *var, size_t length) {
2799 // Make space for '\0'
2800 42249 ++length;
2801
2/2
✓ Branch 0 taken 386 times.
✓ Branch 1 taken 41863 times.
42249 if (var->alloced_len < length) {
2802 // At least double the length, so we don't have to allocate so often.
2803
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 322 times.
386 if (length < var->alloced_len * 2) length = var->alloced_len * 2;
2804 386 var->str_val = (char *)my_realloc(PSI_NOT_INSTRUMENTED, var->str_val,
2805 length, MYF(MY_WME));
2806
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 386 times.
386 if (!var->str_val) die("Out of memory");
2807 386 var->alloced_len = length;
2808 }
2809 42249 }
2810
2811 /**
2812 Process a "let $variable = escape(CHARACTERS,TEXT)" command.
2813
2814 This will parse the text starting after 'escape'. If parsing is
2815 successful, it inserts a backslash character before each character
2816 in 'TEXT' that occurs in 'CHARACTERS' and stores the result in
2817 $variable. If parsing fails, it prints an error message and aborts
2818 the program.
2819
2820 CHARACTERS is a string of at least one character, where ',' must not
2821 occur except as the first character. Newlines are not allowed.
2822 Backslashes are not treated as escape characters when parsing
2823 CHARACTERS, so escape(\',$x) will replace both backslashes and
2824 single quotes in $x.
2825
2826 @param command The st_command structure, where the 'first_argument'
2827 member points to the character just after 'escape'.
2828
2829 @param dst VAR object representing $variable
2830 */
2831 42262 static void var_set_escape(struct st_command *command, VAR *dst) {
2832 /*
2833 Parse and return the arguments.
2834
2835 This expects the format:
2836
2837 ^\([^\n\r][^,\n\r]*,.*\)$
2838
2839 I.e., an opening parenthesis, followed by a newline-free string of
2840 at least one character which does not have commas except possibly
2841 in the first character, followed by a comma, followed by an
2842 arbitrary string, followed by a closing parenthesis.
2843
2844 Returns a triple containing:
2845 - The first string,
2846 - the second string,
2847 - a bool that is false if the parsing succeeded; true if it failed.
2848 */
2849 42262 auto parse_args = [&]() -> auto {
2850 // command->first_argument contains '(characters,text)'
2851 42262 char *p = command->first_argument;
2852 // Find (
2853
2/2
✓ Branch 0 taken 42256 times.
✓ Branch 1 taken 6 times.
42262 if (*p == '(') {
2854 42256 ++p;
2855 // Find [^\n\r][^,\n\r]*
2856 42256 char *char_start = p;
2857
4/6
✓ Branch 0 taken 42256 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42255 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 42255 times.
✗ Branch 5 not taken.
42256 if (*p != '\0' && *p != '\n' && *p != '\r') {
2858 42255 ++p;
2859
6/8
✓ Branch 0 taken 78688 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 36436 times.
✓ Branch 3 taken 42252 times.
✓ Branch 4 taken 36436 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 36436 times.
✗ Branch 7 not taken.
78691 while (*p != '\0' && *p != ',' && *p != '\n' && *p != '\r') ++p;
2860 // Find ,
2861
2/2
✓ Branch 0 taken 42252 times.
✓ Branch 1 taken 3 times.
42255 if (*p == ',') {
2862 42252 size_t char_len = p - char_start;
2863 42252 ++p;
2864 // Find .*$
2865 42252 char *text_start = p;
2866
2/2
✓ Branch 0 taken 2633784 times.
✓ Branch 1 taken 42252 times.
2676036 while (*p != '\0') ++p;
2867 // Find )
2868 42252 --p;
2869
2/2
✓ Branch 0 taken 42249 times.
✓ Branch 1 taken 3 times.
42252 if (*p == ')') {
2870 42249 size_t text_len = p - text_start;
2871
1/2
✓ Branch 0 taken 42249 times.
✗ Branch 1 not taken.
84498 return std::make_tuple(std::string(char_start, char_len),
2872
2/4
✓ Branch 0 taken 42249 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42249 times.
✗ Branch 3 not taken.
126747 std::string(text_start, text_len), false);
2873 }
2874 }
2875 }
2876 }
2877
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
26 return std::make_tuple(std::string(), std::string(), true);
2878 42262 };
2879
2880 42262 std::string chars;
2881 42262 std::string src;
2882 bool error;
2883
1/2
✓ Branch 0 taken 42262 times.
✗ Branch 1 not taken.
42262 std::tie(chars, src, error) = parse_args();
2884 /*
2885 // This is the same, implemented with a regular expression.
2886 // Unfortunately it crashes on windows. Not sure but maybe a bug in
2887 // the regex library on windows?
2888 auto parse_args_regex = [&]() -> auto {
2889 // command->first_argument contains '(characters,text)'
2890 static const std::regex arg_re("^\\((.[^\\r\\n,]*),((?:.|[\\r\\n])*)\\)$",
2891 std::regex::optimize);
2892 // Parse arguments.
2893 std::cmatch arg_match;
2894 if (std::regex_search(command->first_argument, arg_match, arg_re))
2895 return std::make_tuple(std::string(arg_match[1]),
2896 std::string(arg_match[2]),
2897 false);
2898 return std::make_tuple(std::string(), std::string(), true);
2899 };
2900 std::string character_str2, src2;
2901 bool error2;
2902 std::tie(character_str2, src2, error2) = parse_args_regex();
2903 assert(character_str == character_str2);
2904 assert(src == src2);
2905 assert(error == error2);
2906 */
2907
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 42249 times.
42262 if (error)
2908 13 die("Invalid format of 'escape' arguments: <%.100s>",
2909 command->first_argument);
2910
2911 42249 auto begin = chars.begin();
2912 42249 auto end = chars.end();
2913 // Compute length of escaped string
2914 42249 auto dst_len = src.length();
2915
2/2
✓ Branch 0 taken 2591529 times.
✓ Branch 1 taken 42249 times.
2633778 for (char c : src)
2916
3/4
✓ Branch 0 taken 2591529 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 48663 times.
✓ Branch 3 taken 2542866 times.
2591529 if (std::find(begin, end, c) != end) dst_len++;
2917 // Allocate space for escaped string
2918
1/2
✓ Branch 0 taken 42249 times.
✗ Branch 1 not taken.
42249 alloc_var(dst, dst_len);
2919 42249 auto dst_char = dst->str_val;
2920 // Compute escaped string
2921
2/2
✓ Branch 0 taken 2591529 times.
✓ Branch 1 taken 42249 times.
2633778 for (char c : src) {
2922
3/4
✓ Branch 0 taken 2591529 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 48663 times.
✓ Branch 3 taken 2542866 times.
2591529 if (std::find(begin, end, c) != end) *dst_char++ = '\\';
2923 2591529 *dst_char++ = c;
2924 }
2925 42249 dst->str_val_len = dst_len;
2926 42249 }
2927
2928 /*
2929 Set variable from the result of a field in a query
2930
2931 This function is useful when checking for a certain value
2932 in the output from a query that can't be restricted to only
2933 return some values. A very good example of that is most SHOW
2934 commands.
2935
2936 SYNOPSIS
2937 var_set_query_get_value()
2938
2939 DESCRIPTION
2940 let $variable= query_get_value(<query to run>,<column name>,<row no>);
2941
2942 <query to run> - The query that should be sent to the server
2943 <column name> - Name of the column that holds the field be compared
2944 against the expected value
2945 <row no> - Number of the row that holds the field to be
2946 compared against the expected value
2947
2948 */
2949
2950 406878 static void var_set_query_get_value(struct st_command *command, VAR *var) {
2951 long row_no;
2952 406878 int col_no = -1;
2953 406878 MYSQL_RES *res = nullptr;
2954 406878 MYSQL *mysql = &cur_con->mysql;
2955
2956 static DYNAMIC_STRING ds_query;
2957 static DYNAMIC_STRING ds_col;
2958 static DYNAMIC_STRING ds_row;
2959 406878 const struct command_arg query_get_value_args[] = {
2960 {"query", ARG_STRING, true, &ds_query, "Query to run"},
2961 {"column name", ARG_STRING, true, &ds_col, "Name of column"},
2962 {"row number", ARG_STRING, true, &ds_row, "Number for row"}};
2963
2964
1/2
✓ Branch 0 taken 406878 times.
✗ Branch 1 not taken.
406878 DBUG_TRACE;
2965
2966
1/2
✓ Branch 0 taken 406877 times.
✗ Branch 1 not taken.
406878 strip_parentheses(command);
2967
3/8
✓ Branch 0 taken 406877 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 406877 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 406877 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
406877 DBUG_PRINT("info", ("query: %s", command->query));
2968
1/2
✓ Branch 0 taken 406874 times.
✗ Branch 1 not taken.
406877 check_command_args(command, command->first_argument, query_get_value_args,
2969 sizeof(query_get_value_args) / sizeof(struct command_arg),
2970 ',');
2971
2972
3/8
✓ Branch 0 taken 406874 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 406874 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 406874 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
406874 DBUG_PRINT("info", ("query: %s", ds_query.str));
2973
3/8
✓ Branch 0 taken 406874 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 406874 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 406874 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
406874 DBUG_PRINT("info", ("col: %s", ds_col.str));
2974
2975 /* Convert row number to int */
2976
3/4
✓ Branch 0 taken 406874 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 406873 times.
406874 if (!str2int(ds_row.str, 10, (long)0, (long)INT_MAX, &row_no))
2977 1 die("Invalid row number: '%s'", ds_row.str);
2978
3/8
✓ Branch 0 taken 406873 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 406873 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 406873 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
406873 DBUG_PRINT("info", ("row: %s, row_no: %ld", ds_row.str, row_no));
2979
1/2
✓ Branch 0 taken 406873 times.
✗ Branch 1 not taken.
406873 dynstr_free(&ds_row);
2980
2981 /* Remove any surrounding "'s from the query - if there is any */
2982
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 406873 times.
406873 if (strip_surrounding(ds_query.str, '"', '"'))
2983 die("Mismatched \"'s around query '%s'", ds_query.str);
2984
2985 /* Run the query */
2986
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 406870 times.
406873 if (mysql_real_query_wrapper(mysql, ds_query.str,
2987
1/2
✓ Branch 0 taken 406873 times.
✗ Branch 1 not taken.
406873 static_cast<ulong>(ds_query.length))) {
2988
4/8
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
3 handle_error(curr_command, mysql_errno(mysql), mysql_error(mysql),
2989 mysql_sqlstate(mysql), &ds_res);
2990 /* If error was acceptable, return empty string */
2991
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 dynstr_free(&ds_query);
2992
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 dynstr_free(&ds_col);
2993
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 eval_expr(var, "", nullptr);
2994 2 return;
2995 }
2996
2997
3/4
✓ Branch 0 taken 406870 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 406869 times.
406870 if (!(res = mysql_store_result_wrapper(mysql)))
2998 1 die("Query '%s' didn't return a result set", ds_query.str);
2999
3000 {
3001 /* Find column number from the given column name */
3002 uint i;
3003
1/2
✓ Branch 0 taken 406869 times.
✗ Branch 1 not taken.
406869 uint num_fields = mysql_num_fields(res);
3004
1/2
✓ Branch 0 taken 406869 times.
✗ Branch 1 not taken.
406869 MYSQL_FIELD *fields = mysql_fetch_fields(res);
3005
3006
2/2
✓ Branch 0 taken 3589524 times.
✓ Branch 1 taken 2 times.
3589526 for (i = 0; i < num_fields; i++) {
3007
2/2
✓ Branch 0 taken 406867 times.
✓ Branch 1 taken 3182657 times.
3589524 if (std::strcmp(fields[i].name, ds_col.str) == 0 &&
3008
1/2
✓ Branch 0 taken 406867 times.
✗ Branch 1 not taken.
406867 std::strlen(fields[i].name) == ds_col.length) {
3009 406867 col_no = i;
3010 406867 break;
3011 }
3012 }
3013
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 406867 times.
406869 if (col_no == -1) {
3014
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 mysql_free_result_wrapper(res);
3015 2 die("Could not find column '%s' in the result of '%s'", ds_col.str,
3016 ds_query.str);
3017 }
3018
3/8
✓ Branch 0 taken 406867 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 406867 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 406867 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
406867 DBUG_PRINT("info", ("Found column %d with name '%s'", i, fields[i].name));
3019 }
3020
1/2
✓ Branch 0 taken 406867 times.
✗ Branch 1 not taken.
406867 dynstr_free(&ds_col);
3021
3022 {
3023 /* Get the value */
3024 MYSQL_ROW row;
3025 406867 long rows = 0;
3026 406867 const char *value = "No such row";
3027
3028
3/4
✓ Branch 0 taken 808102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 769594 times.
✓ Branch 3 taken 38508 times.
808102 while ((row = mysql_fetch_row_wrapper(res))) {
3029
2/2
✓ Branch 0 taken 368359 times.
✓ Branch 1 taken 401235 times.
769594 if (++rows == row_no) {
3030
3/8
✓ Branch 0 taken 368359 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 368359 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 368359 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
368359 DBUG_PRINT("info", ("At row %ld, column %d is '%s'", row_no, col_no,
3031 row[col_no]));
3032 /* Found the row to get */
3033
2/2
✓ Branch 0 taken 367834 times.
✓ Branch 1 taken 525 times.
368359 if (row[col_no])
3034 367834 value = row[col_no];
3035 else
3036 525 value = "NULL";
3037
3038 368359 break;
3039 }
3040 }
3041
1/2
✓ Branch 0 taken 406867 times.
✗ Branch 1 not taken.
406867 eval_expr(var, value, nullptr, false, false);
3042 }
3043
1/2
✓ Branch 0 taken 406867 times.
✗ Branch 1 not taken.
406867 dynstr_free(&ds_query);
3044
1/2
✓ Branch 0 taken 406867 times.
✗ Branch 1 not taken.
406867 mysql_free_result_wrapper(res);
3045
2/2
✓ Branch 0 taken 406867 times.
✓ Branch 1 taken 2 times.
406869 }
3046
3047 35551983 static void var_copy(VAR *dest, VAR *src) {
3048 35551983 dest->int_val = src->int_val;
3049 35551983 dest->is_int = src->is_int;
3050 35551983 dest->int_dirty = src->int_dirty;
3051
3052 /* Alloc/realloc data for str_val in dest */
3053
3/4
✓ Branch 0 taken 4597712 times.
✓ Branch 1 taken 30954271 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 35551983 times.
40149695 if (dest->alloced_len < src->alloced_len &&
3054
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4597712 times.
4597712 !(dest->str_val =
3055 4597712 dest->str_val
3056
1/2
✓ Branch 0 taken 4597712 times.
✗ Branch 1 not taken.
4597712 ? (char *)my_realloc(PSI_NOT_INSTRUMENTED, dest->str_val,
3057 src->alloced_len, MYF(MY_WME))
3058 : (char *)my_malloc(PSI_NOT_INSTRUMENTED, src->alloced_len,
3059 MYF(MY_WME))))
3060 die("Out of memory");
3061 else
3062 35551983 dest->alloced_len = src->alloced_len;
3063
3064 /* Copy str_val data to dest */
3065 35551983 dest->str_val_len = src->str_val_len;
3066
2/2
✓ Branch 0 taken 30274170 times.
✓ Branch 1 taken 5277813 times.
35551983 if (src->str_val_len) memcpy(dest->str_val, src->str_val, src->str_val_len);
3067 35551983 }
3068
3069 106834421 void eval_expr(VAR *v, const char *p, const char **p_end, bool open_end,
3070 bool do_eval) {
3071
1/2
✓ Branch 0 taken 106834421 times.
✗ Branch 1 not taken.
106834421 DBUG_TRACE;
3072
2/2
✓ Branch 0 taken 106379778 times.
✓ Branch 1 taken 454643 times.
106834421 if (p_end) {
3073
3/8
✓ Branch 0 taken 106379778 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 106379778 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 106379778 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
106379778 DBUG_PRINT("enter", ("p: '%.*s'", (int)(*p_end - p), p));
3074 } else {
3075
3/8
✓ Branch 0 taken 454643 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 454643 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 454643 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
454643 DBUG_PRINT("enter", ("p: '%s'", p));
3076 }
3077 /* Skip to treat as pure string if no evaluation */
3078
2/2
✓ Branch 0 taken 8641917 times.
✓ Branch 1 taken 98192504 times.
106834421 if (!do_eval) goto NO_EVAL;
3079
3080
2/2
✓ Branch 0 taken 35551983 times.
✓ Branch 1 taken 62640521 times.
98192504 if (*p == '$') {
3081 VAR *vp;
3082 35551983 const char *expected_end = *p_end; // Remember var end
3083
3/6
✓ Branch 0 taken 35551983 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35551983 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 35551983 times.
✗ Branch 5 not taken.
35551983 if ((vp = var_get(p, p_end, false, false))) var_copy(v, vp);
3084
3085 /* Apparently it is not safe to assume null-terminated string */
3086 35551983 v->str_val[v->str_val_len] = 0;
3087
3088 /* Make sure there was just a $variable and nothing else */
3089 35551983 const char *end = *p_end + 1;
3090
3/4
✓ Branch 0 taken 12159003 times.
✓ Branch 1 taken 23392980 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12159003 times.
35551983 if (end < expected_end && !open_end)
3091 die("Found junk '%.*s' after $variable in expression",
3092 (int)(expected_end - end - 1), end);
3093
3094 35551983 return;
3095 }
3096
3097
2/2
✓ Branch 0 taken 8281869 times.
✓ Branch 1 taken 54358652 times.
62640521 if (*p == '`') {
3098
1/2
✓ Branch 0 taken 8281865 times.
✗ Branch 1 not taken.
8281869 var_query_set(v, p, p_end);
3099 8281865 return;
3100 }
3101
3102 {
3103 auto parse_function =
3104 162261240 [&](const char *prefix,
3105 void (*parser)(struct st_command *, VAR *)) -> bool {
3106 162261240 const size_t len = std::strlen(prefix);
3107
2/2
✓ Branch 0 taken 450100 times.
✓ Branch 1 taken 161811140 times.
162261240 if (std::strncmp(p, prefix, len) == 0) {
3108 struct st_command command;
3109 450100 memset(&command, 0, sizeof(command));
3110 450100 command.query = const_cast<char *>(p);
3111 450100 command.first_word_len = len;
3112 450100 command.first_argument = command.query + len;
3113 450100 command.end = const_cast<char *>(*p_end);
3114
1/2
✓ Branch 0 taken 450077 times.
✗ Branch 1 not taken.
450100 parser(&command, v);
3115 450077 return true;
3116 }
3117 161811140 return false;
3118 54358652 };
3119
3120
3/4
✓ Branch 0 taken 54358643 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 406869 times.
✓ Branch 3 taken 53951774 times.
54401860 if (parse_function("query_get_value", var_set_query_get_value)) return;
3121
3/4
✓ Branch 0 taken 53951773 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 959 times.
✓ Branch 3 taken 53950814 times.
53951774 if (parse_function("convert_error", var_set_convert_error)) return;
3122
3/4
✓ Branch 0 taken 53950801 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42249 times.
✓ Branch 3 taken 53908552 times.
53950814 if (parse_function("escape", var_set_escape)) return;
3123 }
3124
3125 62550469 NO_EVAL : {
3126 62550469 size_t new_val_len =
3127
3/4
✓ Branch 0 taken 62095826 times.
✓ Branch 1 taken 454643 times.
✓ Branch 2 taken 62095826 times.
✗ Branch 3 not taken.
62550469 (p_end && *p_end) ? static_cast<size_t>(*p_end - p) : std::strlen(p);
3128
2/2
✓ Branch 0 taken 7541409 times.
✓ Branch 1 taken 55009060 times.
62550469 if (new_val_len + 1 >= v->alloced_len) {
3129 static size_t MIN_VAR_ALLOC = 32;
3130 7541409 v->alloced_len =
3131
2/2
✓ Branch 0 taken 4127931 times.
✓ Branch 1 taken 3413478 times.
7541409 (new_val_len < MIN_VAR_ALLOC - 1) ? MIN_VAR_ALLOC : new_val_len + 1;
3132
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7541409 times.
7541409 if (!(v->str_val =
3133
2/2
✓ Branch 0 taken 7541231 times.
✓ Branch 1 taken 178 times.
15082640 v->str_val ? (char *)my_realloc(PSI_NOT_INSTRUMENTED, v->str_val,
3134
1/2
✓ Branch 0 taken 7541231 times.
✗ Branch 1 not taken.
7541231 v->alloced_len + 1, MYF(MY_WME))
3135 178 : (char *)my_malloc(PSI_NOT_INSTRUMENTED,
3136
1/2
✓ Branch 0 taken 178 times.
✗ Branch 1 not taken.
178 v->alloced_len + 1, MYF(MY_WME))))
3137 die("Out of memory");
3138 }
3139 62550469 v->str_val_len = new_val_len;
3140 62550469 memcpy(v->str_val, p, new_val_len);
3141 62550469 v->str_val[new_val_len] = 0;
3142 62550469 var_check_int(v);
3143 }
3144
2/2
✓ Branch 0 taken 62550469 times.
✓ Branch 1 taken 44283925 times.
106834394 }
3145
3146 4441764 static int open_file(const char *name) {
3147 char buff[FN_REFLEN];
3148 size_t length;
3149
1/2
✓ Branch 0 taken 4441764 times.
✗ Branch 1 not taken.
4441764 DBUG_TRACE;
3150
3/8
✓ Branch 0 taken 4441764 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4441764 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4441764 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
4441764 DBUG_PRINT("enter", ("name: %s", name));
3151
3152 4441764 bool file_exists = false;
3153 /* Extract path from current file and try it as base first */
3154
3/4
✓ Branch 0 taken 4441764 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4441757 times.
✓ Branch 3 taken 7 times.
4441764 if (dirname_part(buff, cur_file->file_name, &length)) {
3155
1/2
✓ Branch 0 taken 4441757 times.
✗ Branch 1 not taken.
4441757 strxmov(buff, buff, name, NullS);
3156
2/2
✓ Branch 0 taken 10127 times.
✓ Branch 1 taken 4431630 times.
4441757 if (access(buff, F_OK) == 0) {
3157
3/8
✓ Branch 0 taken 10127 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10127 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 10127 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
10127 DBUG_PRINT("info", ("The file exists"));
3158 10127 name = buff;
3159 10127 file_exists = true;
3160 }
3161 }
3162
3163
7/8
✓ Branch 0 taken 4441764 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4430590 times.
✓ Branch 3 taken 11174 times.
✓ Branch 4 taken 4428640 times.
✓ Branch 5 taken 1950 times.
✓ Branch 6 taken 4428640 times.
✓ Branch 7 taken 13124 times.
4441764 if (!test_if_hard_path(name) && !file_exists) {
3164
1/2
✓ Branch 0 taken 4428640 times.
✗ Branch 1 not taken.
4428640 strxmov(buff, opt_basedir, name, NullS);
3165 4428640 name = buff;
3166 }
3167
1/2
✓ Branch 0 taken 4441764 times.
✗ Branch 1 not taken.
4441764 fn_format(buff, name, "", "", MY_UNPACK_FILENAME);
3168
3169
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4441763 times.
4441764 if (cur_file == file_stack_end) die("Source directives are nesting too deep");
3170 4441763 cur_file++;
3171
3/4
✓ Branch 0 taken 4441763 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4441762 times.
4441763 if (!(cur_file->file = fopen(buff, "rb"))) {
3172 1 cur_file--;
3173 1 die("Could not open '%s' for reading, errno: %d", buff, errno);
3174 }
3175
1/2
✓ Branch 0 taken 4441762 times.
✗ Branch 1 not taken.
4441762 cur_file->file_name = my_strdup(PSI_NOT_INSTRUMENTED, buff, MYF(MY_FAE));
3176 4441762 cur_file->lineno = 1;
3177 4441762 return 0;
3178 4441762 }
3179
3180 /*
3181 Source and execute the given file
3182
3183 SYNOPSIS
3184 do_source()
3185 query called command
3186
3187 DESCRIPTION
3188 source <file_name>
3189
3190 Open the file <file_name> and execute it
3191
3192 */
3193
3194 26971212 static void do_source(struct st_command *command) {
3195 static DYNAMIC_STRING ds_filename;
3196 26971212 const struct command_arg source_args[] = {
3197 {"filename", ARG_STRING, true, &ds_filename, "File to source"}};
3198
1/2
✓ Branch 0 taken 26971212 times.
✗ Branch 1 not taken.
26971212 DBUG_TRACE;
3199
3200
1/2
✓ Branch 0 taken 26971211 times.
✗ Branch 1 not taken.
26971212 check_command_args(command, command->first_argument, source_args,
3201 sizeof(source_args) / sizeof(struct command_arg), ' ');
3202
3203 /*
3204 If this file has already been sourced, don't source it again.
3205 It's already available in the q_lines cache.
3206 */
3207
2/2
✓ Branch 0 taken 4441764 times.
✓ Branch 1 taken 22529447 times.
26971211 if (parser.current_line < (parser.read_lines - 1))
3208 ; /* Do nothing */
3209 else {
3210
3/8
✓ Branch 0 taken 4441764 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4441764 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4441764 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
4441764 DBUG_PRINT("info", ("sourcing file: %s", ds_filename.str));
3211
1/2
✓ Branch 0 taken 4441762 times.
✗ Branch 1 not taken.
4441764 open_file(ds_filename.str);
3212 }
3213
3214
1/2
✓ Branch 0 taken 26971209 times.
✗ Branch 1 not taken.
26971209 dynstr_free(&ds_filename);
3215 26971209 }
3216
3217 38174 static FILE *my_popen(DYNAMIC_STRING *ds_cmd, const char *mode,
3218 struct st_command *command [[maybe_unused]]) {
3219 #ifdef _WIN32
3220 /*
3221 --execw is for tests executing commands containing non-ASCII characters.
3222
3223 To correctly start such a program on Windows, we need to use the "wide"
3224 version of popen, with prior translation of the command line from
3225 the file character set to wide string. We use the current value
3226 of --character_set as a file character set, so before using --execw
3227 make sure to set --character_set properly.
3228
3229 If we use the non-wide version of popen, Windows internally
3230 converts command line from the current ANSI code page to wide string.
3231 In case when character set of the command line does not match the
3232 current ANSI code page, non-ASCII characters get garbled in most cases.
3233
3234 On Linux, the command line passed to popen() is considered
3235 as a binary string, no any internal to-wide and from-wide
3236 character set conversion happens, so we don't need to do anything.
3237 On Linux --execw is just a synonym to --exec.
3238
3239 For simplicity, assume that command line is limited to 4KB
3240 (like in cmd.exe) and that mode at most 10 characters.
3241 */
3242 if (command->type == Q_EXECW) {
3243 wchar_t wcmd[4096];
3244 wchar_t wmode[10];
3245 const char *cmd = ds_cmd->str;
3246 uint dummy_errors;
3247 size_t len;
3248 len = my_convert((char *)wcmd, sizeof(wcmd) - sizeof(wcmd[0]),
3249 &my_charset_utf16le_bin, ds_cmd->str,
3250 std::strlen(ds_cmd->str), charset_info, &dummy_errors);
3251 wcmd[len / sizeof(wchar_t)] = 0;
3252 len = my_convert((char *)wmode, sizeof(wmode) - sizeof(wmode[0]),
3253 &my_charset_utf16le_bin, mode, std::strlen(mode),
3254 charset_info, &dummy_errors);
3255 wmode[len / sizeof(wchar_t)] = 0;
3256 return _wpopen(wcmd, wmode);
3257 }
3258 #endif /* _WIN32 */
3259
3260 38174 errno = 0;
3261 38174 FILE *file = popen(ds_cmd->str, mode);
3262
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38174 times.
38174 if (file == NULL) {
3263 if (errno != 0) {
3264 fprintf(stderr, "mysqltest: popen failed with errno %d (%s)\n", errno,
3265 strerror(errno));
3266 } else {
3267 fprintf(stderr,
3268 "mysqltest: popen returned NULL without setting errno "
3269 "(out-of-memory?)\n");
3270 }
3271 }
3272 38174 return file;
3273 }
3274
3275 55515 static void init_builtin_echo(void) {
3276 #ifdef _WIN32
3277 size_t echo_length;
3278
3279 /* Look for "echo.exe" in same dir as mysqltest was started from */
3280 dirname_part(builtin_echo, my_progname, &echo_length);
3281 fn_format(builtin_echo, ".\\echo.exe", builtin_echo, "", MYF(MY_REPLACE_DIR));
3282
3283 /* Make sure echo.exe exists */
3284 if (access(builtin_echo, F_OK) != 0) builtin_echo[0] = 0;
3285 return;
3286
3287 #else
3288
3289 55515 builtin_echo[0] = 0;
3290 55515 return;
3291
3292 #endif
3293 }
3294
3295 /*
3296 Replace a substring
3297
3298 SYNOPSIS
3299 replace
3300 ds_str The string to search and perform the replace in
3301 search_str The string to search for
3302 search_len Length of the string to search for
3303 replace_str The string to replace with
3304 replace_len Length of the string to replace with
3305
3306 RETURN
3307 0 String replaced
3308 1 Could not find search_str in str
3309 */
3310
3311 42356 static int replace(DYNAMIC_STRING *ds_str, const char *search_str,
3312 size_t search_len, const char *replace_str,
3313 size_t replace_len) {
3314 DYNAMIC_STRING ds_tmp;
3315 42356 const char *start = strstr(ds_str->str, search_str);
3316
2/2
✓ Branch 0 taken 38174 times.
✓ Branch 1 taken 4182 times.
42356 if (!start) return 1;
3317
1/2
✓ Branch 0 taken 4182 times.
✗ Branch 1 not taken.
4182 init_dynamic_string(&ds_tmp, "", ds_str->length + replace_len);
3318
1/2
✓ Branch 0 taken 4182 times.
✗ Branch 1 not taken.
4182 dynstr_append_mem(&ds_tmp, ds_str->str, start - ds_str->str);
3319
1/2
✓ Branch 0 taken 4182 times.
✗ Branch 1 not taken.
4182 dynstr_append_mem(&ds_tmp, replace_str, replace_len);
3320
1/2
✓ Branch 0 taken 4182 times.
✗ Branch 1 not taken.
4182 dynstr_append(&ds_tmp, start + search_len);
3321
1/2
✓ Branch 0 taken 4182 times.
✗ Branch 1 not taken.
4182 dynstr_set(ds_str, ds_tmp.str);
3322
1/2
✓ Branch 0 taken 4182 times.
✗ Branch 1 not taken.
4182 dynstr_free(&ds_tmp);
3323 4182 return 0;
3324 }
3325
3326 #ifdef _WIN32
3327 /**
3328 Replace CRLF sequence with LF in place.
3329
3330 This function is required as a workaround for a bug in the Microsoft
3331 C runtime library introduced in Visual Studio 2015.
3332 See bug#22608247 and bug#22811243
3333
3334 @param buf Null terminated buffer.
3335 */
3336 static void replace_crlf_with_lf(char *buf) {
3337 char *replace = buf;
3338 while (*buf) {
3339 *replace = *buf++;
3340 if (!((*replace == '\x0D') && (*buf == '\x0A'))) {
3341 replace++;
3342 }
3343 }
3344 *replace = '\x0';
3345 }
3346 #endif
3347
3348 /// Execute the shell command using the popen() library call. References
3349 /// to variables within the command are replaced with the corresponding
3350 /// values. Use “\\$” to specify a literal “$” character.
3351 ///
3352 /// The error code returned from the subprocess is checked against the
3353 /// expected error array, previously set with the --error command. It can
3354 /// thus be used to execute a command that shall fail.
3355 ///
3356 /// @code
3357 /// exec command [args]
3358 /// @endcode
3359 ///
3360 /// @param command Pointer to the st_command structure which holds the
3361 /// arguments and information for the command.
3362 /// @param run_in_background Specifies if command should be run in background.
3363 /// In such case we don't wait nor attempt to read the
3364 /// output.
3365 ///
3366 /// @note
3367 /// It is recommended to use mysqltest command(s) like "remove_file"
3368 /// instead of executing the shell commands using 'exec' command.
3369 38175 static void do_exec(struct st_command *command, bool run_in_background) {
3370
1/2
✓ Branch 0 taken 38175 times.
✗ Branch 1 not taken.
38175 DBUG_TRACE;
3371
3372 38175 const char *cmd = command->first_argument;
3373
3/8
✓ Branch 0 taken 38175 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38175 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 38175 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
38175 DBUG_PRINT("enter", ("cmd: '%s'", cmd));
3374
3375 // Skip leading space
3376
3/4
✓ Branch 0 taken 38174 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 38174 times.
38175 while (*cmd && my_isspace(charset_info, *cmd)) cmd++;
3377
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 38174 times.
38175 if (!*cmd) die("Missing argument in exec");
3378 38174 command->last_argument = command->end;
3379
3380 DYNAMIC_STRING ds_cmd;
3381
1/2
✓ Branch 0 taken 38174 times.
✗ Branch 1 not taken.
38174 init_dynamic_string(&ds_cmd, nullptr, command->query_len + 256);
3382
3383 // Eval the command, thus replacing all environment variables
3384
1/2
✓ Branch 0 taken 38174 times.
✗ Branch 1 not taken.
38174 do_eval(&ds_cmd, cmd, command->end, !is_windows);
3385
3386 // Check if echo should be replaced with "builtin" echo
3387
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 38174 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
38174 if (builtin_echo[0] && std::strncmp(cmd, "echo", 4) == 0) {
3388 // Replace echo with our "builtin" echo
3389 replace(&ds_cmd, "echo", 4, builtin_echo, std::strlen(builtin_echo));
3390 }
3391
3392 #ifdef _WIN32
3393 // Replace "/dev/null" with NUL
3394 while (replace(&ds_cmd, "/dev/null", 9, "NUL", 3) == 0)
3395 ;
3396
3397 // Replace "closed stdout" with non existing output fd
3398 while (replace(&ds_cmd, ">&-", 3, ">&4", 3) == 0)
3399 ;
3400 #endif
3401
3402
2/2
✓ Branch 0 taken 10648 times.
✓ Branch 1 taken 27526 times.
38174 if (run_in_background) {
3403 /* Add an invocation of "START /B" on Windows, append " &" on Linux*/
3404 DYNAMIC_STRING ds_tmp;
3405 #ifdef WIN32
3406 init_dynamic_string(&ds_tmp, "START /B ", ds_cmd.length + 9);
3407 dynstr_append_mem(&ds_tmp, ds_cmd.str, ds_cmd.length);
3408 #else
3409
1/2
✓ Branch 0 taken 10648 times.
✗ Branch 1 not taken.
10648 init_dynamic_string(&ds_tmp, ds_cmd.str, ds_cmd.length + 2);
3410
1/2
✓ Branch 0 taken 10648 times.
✗ Branch 1 not taken.
10648 dynstr_append_mem(&ds_tmp, " &", 2);
3411 #endif
3412
1/2
✓ Branch 0 taken 10648 times.
✗ Branch 1 not taken.
10648 dynstr_set(&ds_cmd, ds_tmp.str);
3413
1/2
✓ Branch 0 taken 10648 times.
✗ Branch 1 not taken.
10648 dynstr_free(&ds_tmp);
3414 }
3415
3416 // exec command is interpreted externally and will not take newlines
3417
3/4
✓ Branch 0 taken 42356 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4182 times.
✓ Branch 3 taken 38174 times.
42356 while (replace(&ds_cmd, "\n", 1, " ", 1) == 0)
3418 ;
3419
3420
3/8
✓ Branch 0 taken 38174 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38174 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 38174 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
38174 DBUG_PRINT("info",
3421 ("Executing '%s' as '%s'", command->first_argument, ds_cmd.str));
3422
3423 #ifdef WIN32
3424 // Open pipe in binary mode as part of handling Microsoft _read bug.
3425 // See bug#22608247 and bug#22811243
3426 const char *mode = "rb";
3427 #else
3428 38174 const char *mode = "r";
3429 #endif
3430 FILE *res_file;
3431
3/6
✓ Branch 0 taken 38174 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 38174 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 38174 times.
38174 if (!(res_file = my_popen(&ds_cmd, mode, command)) &&
3432 command->abort_on_error) {
3433 dynstr_free(&ds_cmd);
3434 die("popen(\"%s\", \"r\") failed", command->first_argument);
3435 }
3436
3437
2/2
✓ Branch 0 taken 27526 times.
✓ Branch 1 taken 10648 times.
38174 if (!run_in_background) {
3438 char buf[512];
3439 27526 std::string str;
3440
3/4
✓ Branch 0 taken 243270 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 215744 times.
✓ Branch 3 taken 27526 times.
243270 while (std::fgets(buf, sizeof(buf), res_file)) {
3441
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 215743 times.
215744 if (std::strlen(buf) < 1) continue;
3442
3443 #ifdef WIN32
3444 // Replace CRLF char with LF.
3445 // See bug#22608247 and bug#22811243
3446 assert(!std::strcmp(mode, "rb"));
3447 replace_crlf_with_lf(buf);
3448 #endif
3449
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 215743 times.
215743 if (trace_exec) {
3450 fprintf(stdout, "%s", buf);
3451 fflush(stdout);
3452 }
3453
2/2
✓ Branch 0 taken 36171 times.
✓ Branch 1 taken 179572 times.
215743 if (disable_result_log) {
3454 36171 buf[std::strlen(buf) - 1] = 0;
3455
3/8
✓ Branch 0 taken 36171 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36171 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 36171 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
36171 DBUG_PRINT("exec_result", ("%s", buf));
3456 } else {
3457 // Read the file line by line. Check if the buffer read from the
3458 // file ends with EOL character.
3459
2/2
✓ Branch 0 taken 562 times.
✓ Branch 1 taken 179010 times.
179572 if ((buf[std::strlen(buf) - 1] != '\n' &&
3460
2/2
✓ Branch 0 taken 558 times.
✓ Branch 1 taken 4 times.
562 std::strlen(buf) < (sizeof(buf) - 1)) ||
3461
2/2
✓ Branch 0 taken 179010 times.
✓ Branch 1 taken 558 times.
179568 (buf[std::strlen(buf) - 1] == '\n')) {
3462 // Found EOL
3463
2/2
✓ Branch 0 taken 268 times.
✓ Branch 1 taken 178746 times.
179014 if (str.length()) {
3464 // Temporary string exists, append the current buffer read
3465 // to the temporary string.
3466
1/2
✓ Branch 0 taken 268 times.
✗ Branch 1 not taken.
268 str.append(buf);
3467
1/2
✓ Branch 0 taken 268 times.
✗ Branch 1 not taken.
268 replace_dynstr_append(&ds_res, str.c_str());
3468 268 str.clear();
3469 } else {
3470 // Entire line is read at once
3471
1/2
✓ Branch 0 taken 178746 times.
✗ Branch 1 not taken.
178746 replace_dynstr_append(&ds_res, buf);
3472 }
3473 } else {
3474 // The buffer read from the file doesn't end with EOL character,
3475 // store it in a temporary string.
3476
1/2
✓ Branch 0 taken 558 times.
✗ Branch 1 not taken.
558 str.append(buf);
3477 }
3478 }
3479 }
3480 27526 }
3481
3482 38174 std::uint32_t status = 0;
3483
1/2
✓ Branch 0 taken 38174 times.
✗ Branch 1 not taken.
38174 int error = pclose(res_file);
3484
3485
2/2
✓ Branch 0 taken 1338 times.
✓ Branch 1 taken 36836 times.
38174 if (error != 0) {
3486 #ifdef _WIN32
3487 status = WEXITSTATUS(error);
3488 #else
3489
1/2
✓ Branch 0 taken 1338 times.
✗ Branch 1 not taken.
1338 if (error > 0) {
3490 // Do the same as many shells here: show SIGKILL as 137
3491
1/2
✓ Branch 0 taken 1338 times.
✗ Branch 1 not taken.
1338 if (WIFEXITED(error))
3492 1338 status = WEXITSTATUS(error);
3493 else if (WIFSIGNALED(error))
3494 status = 0x80 + WTERMSIG(error);
3495 }
3496 #endif
3497
3498
2/2
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 1286 times.
1338 if (command->abort_on_error) {
3499 52 log_msg("exec of '%s' failed, error: %d, status: %d, errno: %d.",
3500
1/2
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
52 ds_cmd.str, error, status, errno);
3501
1/2
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
52 dynstr_free(&ds_cmd);
3502 52 die("Command \"%s\" failed.\n\nOutput from before failure:\n%s",
3503 command->first_argument, ds_res.str);
3504 }
3505
3506
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1286 times.
1286 if (status == 0) status = error;
3507 }
3508
3509
1/2
✓ Branch 0 taken 38122 times.
✗ Branch 1 not taken.
38122 dynstr_free(&ds_cmd);
3510
1/2
✓ Branch 0 taken 38120 times.
✗ Branch 1 not taken.
38122 handle_command_error(command, status);
3511
3512 // Save error code
3513
1/2
✓ Branch 0 taken 38120 times.
✗ Branch 1 not taken.
38120 save_error_code(error);
3514 38120 }
3515
3516 enum enum_operator { DO_DEC, DO_INC };
3517
3518 /// Template function that frees memory of the dynamic string
3519 /// passed to the function.
3520 ///
3521 /// @param val Dynamic string whose memory needs to be freed.
3522 template <typename T>
3523 299353 static void free_dynamic_strings(T *val) {
3524 299353 dynstr_free(val);
3525 299353 }
3526
3527 /// Frees the memory of dynamic strings passed to the function.
3528 /// It accepts a variable number of dynamic strings, and through
3529 /// recursion, frees the memory. The other template function
3530 /// which calls dynstr_free() is called here.
3531 ///
3532 /// @param first The dynamic string passed to the function which
3533 /// gets freed using dynstr_free().
3534 /// @param rest Rest of the dynamic strings which are passed to
3535 /// the function, through recursion, end up being
3536 /// freed by dynstr_free().
3537 template <typename T1, typename... T2>
3538 305354 static void free_dynamic_strings(T1 *first, T2 *... rest) {
3539 305354 free_dynamic_strings(first);
3540 305354 free_dynamic_strings(rest...);
3541 305354 }
3542
3543 /*
3544 Decrease or increase the value of a variable
3545
3546 SYNOPSIS
3547 do_modify_var()
3548 query called command
3549 op operation to perform on the var
3550
3551 DESCRIPTION
3552 dec $var_name
3553 inc $var_name
3554
3555 */
3556
3557 9745690 static int do_modify_var(struct st_command *command, enum enum_operator op) {
3558 9745690 const char *p = command->first_argument;
3559 VAR *v;
3560
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 9745688 times.
9745690 if (!*p)
3561 2 die("Missing argument to %.*s", static_cast<int>(command->first_word_len),
3562 command->query);
3563
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 9745686 times.
9745688 if (*p != '$')
3564 2 die("The argument to %.*s must be a variable (start with $)",
3565 2 static_cast<int>(command->first_word_len), command->query);
3566
1/2
✓ Branch 0 taken 9745686 times.
✗ Branch 1 not taken.
9745686 v = var_get(p, &p, true, false);
3567
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 9745680 times.
9745686 if (!v->is_int) die("Cannot perform inc/dec on a non-numeric value");
3568
2/3
✓ Branch 0 taken 4225929 times.
✓ Branch 1 taken 5519751 times.
✗ Branch 2 not taken.
9745680 switch (op) {
3569 4225929 case DO_DEC:
3570 4225929 v->int_val--;
3571 4225929 break;
3572 5519751 case DO_INC:
3573 5519751 v->int_val++;
3574 5519751 break;
3575 default:
3576 die("Invalid operator to do_modify_var");
3577 break;
3578 }
3579 9745680 v->int_dirty = true;
3580 9745680 command->last_argument = const_cast<char *>(++p);
3581 9745680 return 0;
3582 }
3583
3584 /// Removes the file passed as the argument and retries a specified
3585 /// number of times, if it is unsuccessful.
3586 ///
3587 /// @param command Pointer to the st_command structure which holds the
3588 /// arguments and information for the command.
3589 122176 static void do_remove_file(struct st_command *command) {
3590 int error;
3591 static DYNAMIC_STRING ds_filename;
3592 static DYNAMIC_STRING ds_retry;
3593
3594 122176 const struct command_arg rm_args[] = {
3595 {"filename", ARG_STRING, true, &ds_filename, "File to delete"},
3596 {"retry", ARG_STRING, false, &ds_retry, "Number of retries"}};
3597
1/2
✓ Branch 0 taken 122176 times.
✗ Branch 1 not taken.
122176 DBUG_TRACE;
3598
3599
1/2
✓ Branch 0 taken 122175 times.
✗ Branch 1 not taken.
122176 check_command_args(command, command->first_argument, rm_args,
3600 sizeof(rm_args) / sizeof(struct command_arg), ' ');
3601
3602 // Check if the retry value is passed, and if it is an integer
3603 122175 int retry = 0;
3604
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 122158 times.
122175 if (ds_retry.length) {
3605
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 retry = get_int_val(ds_retry.str);
3606
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 12 times.
17 if (retry < 0) {
3607 // In case of invalid retry, copy the value passed to print later
3608 char buf[32];
3609
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 strmake(buf, ds_retry.str, sizeof(buf) - 1);
3610
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 free_dynamic_strings(&ds_filename, &ds_retry);
3611 5 die("Invalid value '%s' for retry argument given to remove_file "
3612 "command.",
3613 buf);
3614 }
3615 }
3616
3617
3/8
✓ Branch 0 taken 122170 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 122170 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 122170 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
122170 DBUG_PRINT("info", ("removing file: %s", ds_filename.str));
3618
1/2
✓ Branch 0 taken 122170 times.
✗ Branch 1 not taken.
122170 error = my_delete(ds_filename.str, MYF(0)) != 0;
3619
3620 /*
3621 If the remove command fails due to an environmental issue, the command can
3622 be retried a specified number of times before throwing an error.
3623 */
3624
3/4
✓ Branch 0 taken 33459 times.
✓ Branch 1 taken 88711 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 33459 times.
122170 for (int i = 0; error && (i < retry); i++) {
3625 my_sleep(1000 * 1000);
3626 error = my_delete(ds_filename.str, MYF(0)) != 0;
3627 }
3628
3629
1/2
✓ Branch 0 taken 122169 times.
✗ Branch 1 not taken.
122170 handle_command_error(command, error);
3630
1/2
✓ Branch 0 taken 122169 times.
✗ Branch 1 not taken.
122169 free_dynamic_strings(&ds_filename, &ds_retry);
3631 122169 }
3632
3633 /// Removes the files in the specified directory, by matching the
3634 /// file name pattern. Retry of the command can happen optionally with
3635 /// an interval of one second between each retry if the command fails.
3636 ///
3637 /// @param command Pointer to the st_command structure which holds the
3638 /// arguments and information for the command.
3639 652 static void do_remove_files_wildcard(struct st_command *command) {
3640 652 int error = 0;
3641 uint i;
3642 MY_DIR *dir_info;
3643 FILEINFO *file;
3644 char dir_separator[2];
3645 static DYNAMIC_STRING ds_directory;
3646 static DYNAMIC_STRING ds_wild;
3647 static DYNAMIC_STRING ds_retry;
3648 char dirname[FN_REFLEN];
3649
3650 652 const struct command_arg rm_args[] = {
3651 {"directory", ARG_STRING, true, &ds_directory,
3652 "Directory containing files to delete"},
3653 {"pattern", ARG_STRING, true, &ds_wild, "File pattern to delete"},
3654 {"retry", ARG_STRING, false, &ds_retry, "Number of retries"}};
3655
1/2
✓ Branch 0 taken 652 times.
✗ Branch 1 not taken.
652 DBUG_TRACE;
3656
3657
1/2
✓ Branch 0 taken 650 times.
✗ Branch 1 not taken.
652 check_command_args(command, command->first_argument, rm_args,
3658 sizeof(rm_args) / sizeof(struct command_arg), ' ');
3659
1/2
✓ Branch 0 taken 650 times.
✗ Branch 1 not taken.
650 fn_format(dirname, ds_directory.str, "", "", MY_UNPACK_FILENAME);
3660
3661 // Check if the retry value is passed, and if it is an integer
3662 650 int retry = 0;
3663
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 644 times.
650 if (ds_retry.length) {
3664
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 retry = get_int_val(ds_retry.str);
3665
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (retry < 0) {
3666 // In case of invalid retry, copy the value passed to print later
3667 char buf[32];
3668
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 strmake(buf, ds_retry.str, sizeof(buf) - 1);
3669
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 free_dynamic_strings(&ds_directory, &ds_wild, &ds_retry);
3670 5 die("Invalid value '%s' for retry argument given to "
3671 "remove_files_wildcard command.",
3672 buf);
3673 }
3674 }
3675
3676 static DYNAMIC_STRING ds_file_to_remove;
3677
3/8
✓ Branch 0 taken 645 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 645 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 645 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
645 DBUG_PRINT("info", ("listing directory: %s", dirname));
3678 /* Note that my_dir sorts the list if not given any flags */
3679
3/4
✓ Branch 0 taken 645 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 643 times.
645 if (!(dir_info = my_dir(dirname, MYF(MY_DONT_SORT | MY_WANT_STAT)))) {
3680 2 error = 1;
3681 2 goto end;
3682 }
3683
1/2
✓ Branch 0 taken 643 times.
✗ Branch 1 not taken.
643 init_dynamic_string(&ds_file_to_remove, dirname, 1024);
3684 643 dir_separator[0] = FN_LIBCHAR;
3685 643 dir_separator[1] = 0;
3686
1/2
✓ Branch 0 taken 643 times.
✗ Branch 1 not taken.
643 dynstr_append(&ds_file_to_remove, dir_separator);
3687
3688 size_t length;
3689 /* Storing the length of the path to the file, so it can be reused */
3690 643 length = ds_file_to_remove.length;
3691
2/2
✓ Branch 0 taken 7800 times.
✓ Branch 1 taken 643 times.
8443 for (i = 0; i < (uint)dir_info->number_off_files; i++) {
3692 7800 ds_file_to_remove.length = length;
3693 7800 file = dir_info->dir_entry + i;
3694 /* Remove only regular files, i.e. no directories etc. */
3695 /* if (!MY_S_ISREG(file->mystat->st_mode)) */
3696 /* MY_S_ISREG does not work here on Windows, just skip directories */
3697
2/2
✓ Branch 0 taken 2306 times.
✓ Branch 1 taken 5494 times.
7800 if (MY_S_ISDIR(file->mystat->st_mode)) continue;
3698
2/2
✓ Branch 0 taken 2821 times.
✓ Branch 1 taken 2673 times.
5494 if (wild_compare_full(file->name, std::strlen(file->name), ds_wild.str,
3699
1/2
✓ Branch 0 taken 5494 times.
✗ Branch 1 not taken.
5494 std::strlen(ds_wild.str), false, 0, '?', '*'))
3700 2821 continue;
3701 /* Not required as the var ds_file_to_remove.length already has the
3702 length in canonnicalized form */
3703 /* ds_file_to_remove.length= ds_directory.length + 1;
3704 ds_file_to_remove.str[ds_directory.length + 1]= 0; */
3705
1/2
✓ Branch 0 taken 2673 times.
✗ Branch 1 not taken.
2673 dynstr_append(&ds_file_to_remove, file->name);
3706
3/8
✓ Branch 0 taken 2673 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2673 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2673 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
2673 DBUG_PRINT("info", ("removing file: %s", ds_file_to_remove.str));
3707
1/2
✓ Branch 0 taken 2673 times.
✗ Branch 1 not taken.
2673 error = my_delete(ds_file_to_remove.str, MYF(0)) != 0;
3708
3709 /*
3710 If the remove command fails due to an environmental issue, the command
3711 can be retried a specified number of times before throwing an error.
3712 */
3713
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2673 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2673 for (int j = 0; error && (j < retry); j++) {
3714 my_sleep(1000 * 1000);
3715 error = my_delete(ds_file_to_remove.str, MYF(0)) != 0;
3716 }
3717
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2673 times.
2673 if (error) break;
3718 }
3719
1/2
✓ Branch 0 taken 643 times.
✗ Branch 1 not taken.
643 my_dirend(dir_info);
3720
3721 645 end:
3722
1/2
✓ Branch 0 taken 645 times.
✗ Branch 1 not taken.
645 handle_command_error(command, error);
3723
1/2
✓ Branch 0 taken 645 times.
✗ Branch 1 not taken.
645 free_dynamic_strings(&ds_directory, &ds_wild, &ds_file_to_remove, &ds_retry);
3724 645 }
3725
3726 /// Copy the source file to destination file. Copy will fail if the
3727 /// destination file exists. Retry of the command can happen optionally with
3728 /// an interval of one second between each retry if the command fails.
3729 ///
3730 /// @param command Pointer to the st_command structure which holds the
3731 /// arguments and information for the command.
3732 4209 static void do_copy_file(struct st_command *command) {
3733 int error;
3734 static DYNAMIC_STRING ds_from_file;
3735 static DYNAMIC_STRING ds_to_file;
3736 static DYNAMIC_STRING ds_retry;
3737
3738 4209 const struct command_arg copy_file_args[] = {
3739 {"from_file", ARG_STRING, true, &ds_from_file, "Filename to copy from"},
3740 {"to_file", ARG_STRING, true, &ds_to_file, "Filename to copy to"},
3741 {"retry", ARG_STRING, false, &ds_retry, "Number of retries"}};
3742
1/2
✓ Branch 0 taken 4209 times.
✗ Branch 1 not taken.
4209 DBUG_TRACE;
3743
3744
1/2
✓ Branch 0 taken 4207 times.
✗ Branch 1 not taken.
4209 check_command_args(command, command->first_argument, copy_file_args,
3745 sizeof(copy_file_args) / sizeof(struct command_arg), ' ');
3746
3747 // Check if the retry value is passed, and if it is an integer
3748 4207 int retry = 0;
3749
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4201 times.
4207 if (ds_retry.length) {
3750
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 retry = get_int_val(ds_retry.str);
3751
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (retry < 0) {
3752 // In case of invalid retry, copy the value passed to print later
3753 char buf[32];
3754
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 strmake(buf, ds_retry.str, sizeof(buf) - 1);
3755
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 free_dynamic_strings(&ds_from_file, &ds_to_file, &ds_retry);
3756 5 die("Invalid value '%s' for retry argument given to copy_file "
3757 "command.",
3758 buf);
3759 }
3760 }
3761
3762
3/8
✓ Branch 0 taken 4202 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4202 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4202 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
4202 DBUG_PRINT("info", ("Copy %s to %s", ds_from_file.str, ds_to_file.str));
3763 /* MY_HOLD_ORIGINAL_MODES prevents attempts to chown the file */
3764
1/2
✓ Branch 0 taken 4202 times.
✗ Branch 1 not taken.
4202 error = (my_copy(ds_from_file.str, ds_to_file.str,
3765 4202 MYF(MY_DONT_OVERWRITE_FILE | MY_HOLD_ORIGINAL_MODES)) != 0);
3766
3767 /*
3768 If the copy command fails due to an environmental issue, the command can
3769 be retried a specified number of times before throwing an error.
3770 */
3771
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4202 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4202 for (int i = 0; error && (i < retry); i++) {
3772 my_sleep(1000 * 1000);
3773 error =
3774 (my_copy(ds_from_file.str, ds_to_file.str,
3775 MYF(MY_DONT_OVERWRITE_FILE | MY_HOLD_ORIGINAL_MODES)) != 0);
3776 }
3777
3778
1/2
✓ Branch 0 taken 4202 times.
✗ Branch 1 not taken.
4202 handle_command_error(command, error);
3779
1/2
✓ Branch 0 taken 4202 times.
✗ Branch 1 not taken.
4202 free_dynamic_strings(&ds_from_file, &ds_to_file, &ds_retry);
3780 4202 }
3781
3782 /*
3783 SYNOPSIS
3784 recursive_copy
3785 ds_source - pointer to dynamic string containing source
3786 directory information
3787 ds_destination - pointer to dynamic string containing destination
3788 directory information
3789
3790 DESCRIPTION
3791 Recursive copy of <ds_source> to <ds_destination>
3792 */
3793
3794 530 static int recursive_copy(DYNAMIC_STRING *ds_source,
3795 DYNAMIC_STRING *ds_destination) {
3796 /* Note that my_dir sorts the list if not given any flags */
3797 MY_DIR *src_dir_info =
3798 530 my_dir(ds_source->str, MYF(MY_DONT_SORT | MY_WANT_STAT));
3799
3800 530 int error = 0;
3801
3802 /* Source directory exists */
3803
2/2
✓ Branch 0 taken 529 times.
✓ Branch 1 taken 1 times.
530 if (src_dir_info) {
3804 /* Note that my_dir sorts the list if not given any flags */
3805 MY_DIR *dest_dir_info =
3806
1/2
✓ Branch 0 taken 529 times.
✗ Branch 1 not taken.
529 my_dir(ds_destination->str, MYF(MY_DONT_SORT | MY_WANT_STAT));
3807
3808 /* Create destination directory if it doesn't exist */
3809
2/2
✓ Branch 0 taken 519 times.
✓ Branch 1 taken 10 times.
529 if (!dest_dir_info) {
3810
1/2
✓ Branch 0 taken 519 times.
✗ Branch 1 not taken.
519 error = my_mkdir(ds_destination->str, 0777, MYF(0)) != 0;
3811
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 518 times.
519 if (error) {
3812
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 my_dirend(dest_dir_info);
3813 1 goto end;
3814 }
3815 } else {
3816 /* Extracting the source directory name */
3817
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
10 if (ds_source->str[std::strlen(ds_source->str) - 1] == '/') {
3818 9 strmake(ds_source->str, ds_source->str,
3819
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 std::strlen(ds_source->str) - 1);
3820 9 ds_source->length = ds_source->length - 1;
3821 }
3822 10 char *src_dir_name = strrchr(ds_source->str, '/');
3823
3824 /* Extracting the destination directory name */
3825
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (ds_destination->str[std::strlen(ds_destination->str) - 1] == '/') {
3826 strmake(ds_destination->str, ds_destination->str,
3827 std::strlen(ds_destination->str) - 1);
3828 ds_destination->length = ds_destination->length - 1;
3829 }
3830 10 char *dest_dir_name = strrchr(ds_destination->str, '/');
3831
3832 /*
3833 Destination directory might not exist if source directory
3834 name and destination directory name are not same.
3835
3836 For example, if source is "abc" and destintion is "def",
3837 check for the existence of directory "def/abc". If it exists
3838 then, copy the files from source directory(i.e "abc") to
3839 destination directory(i.e "def/abc"), otherwise create a new
3840 directory "abc" under "def" and copy the files from source to
3841 destination directory.
3842 */
3843
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
10 if (std::strcmp(src_dir_name, dest_dir_name)) {
3844
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 dynstr_append(ds_destination, src_dir_name);
3845
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 my_dirend(dest_dir_info);
3846 dest_dir_info =
3847
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 my_dir(ds_destination->str, MYF(MY_DONT_SORT | MY_WANT_STAT));
3848
3849 /* Create destination directory if it doesn't exist */
3850
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
9 if (!dest_dir_info) {
3851
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 error = my_mkdir(ds_destination->str, 0777, MYF(0)) != 0;
3852
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (error) {
3853 my_dirend(dest_dir_info);
3854 goto end;
3855 }
3856 }
3857 }
3858 }
3859
3860 528 char dir_separator[2] = {FN_LIBCHAR, 0};
3861
1/2
✓ Branch 0 taken 528 times.
✗ Branch 1 not taken.
528 dynstr_append(ds_source, dir_separator);
3862
1/2
✓ Branch 0 taken 528 times.
✗ Branch 1 not taken.
528 dynstr_append(ds_destination, dir_separator);
3863
3864 /*
3865 Storing the length of source and destination
3866 directory paths so it can be reused.
3867 */
3868 528 size_t source_dir_length = ds_source->length;
3869 528 size_t destination_dir_length = ds_destination->length;
3870 ;
3871
3872
2/2
✓ Branch 0 taken 13471 times.
✓ Branch 1 taken 528 times.
13999 for (uint i = 0; i < src_dir_info->number_off_files; i++) {
3873 13471 ds_source->length = source_dir_length;
3874 13471 ds_destination->length = destination_dir_length;
3875 13471 FILEINFO *file = src_dir_info->dir_entry + i;
3876
3877 /* Skip the names "." and ".." */
3878
4/4
✓ Branch 0 taken 12943 times.
✓ Branch 1 taken 528 times.
✓ Branch 2 taken 528 times.
✓ Branch 3 taken 12415 times.
13471 if (!std::strcmp(file->name, ".") || !std::strcmp(file->name, ".."))
3879 1056 continue;
3880
3881
1/2
✓ Branch 0 taken 12415 times.
✗ Branch 1 not taken.
12415 dynstr_append(ds_source, file->name);
3882
1/2
✓ Branch 0 taken 12415 times.
✗ Branch 1 not taken.
12415 dynstr_append(ds_destination, file->name);
3883
3884
2/2
✓ Branch 0 taken 470 times.
✓ Branch 1 taken 11945 times.
12415 if (MY_S_ISDIR(file->mystat->st_mode))
3885
3/4
✓ Branch 0 taken 470 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 469 times.
✓ Branch 3 taken 1 times.
470 error = (recursive_copy(ds_source, ds_destination) != 0) ? 1 : error;
3886 else {
3887
3/8
✓ Branch 0 taken 11945 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11945 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 11945 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
11945 DBUG_PRINT("info", ("Copying file: %s to %s", ds_source->str,
3888 ds_destination->str));
3889
3890 /* MY_HOLD_ORIGINAL_MODES prevents attempts to chown the file */
3891
1/2
✓ Branch 0 taken 11945 times.
✗ Branch 1 not taken.
23890 error = (my_copy(ds_source->str, ds_destination->str,
3892 MYF(MY_HOLD_ORIGINAL_MODES)) != 0)
3893
1/2
✓ Branch 0 taken 11945 times.
✗ Branch 1 not taken.
11945 ? 1
3894 : error;
3895 }
3896 }
3897
1/2
✓ Branch 0 taken 528 times.
✗ Branch 1 not taken.
528 my_dirend(dest_dir_info);
3898 }
3899 /* Source directory does not exist or access denied */
3900 else
3901 1 error = 1;
3902
3903 530 end:
3904 530 my_dirend(src_dir_info);
3905 530 return error;
3906 }
3907
3908 /*
3909 SYNOPSIS
3910 do_force_cpdir
3911 command - command handle
3912
3913 DESCRIPTION
3914 force-cpdir <from_directory> <to_directory>
3915 Recursive copy of <from_directory> to <to_directory>.
3916 Destination directory is created if it doesn't exist.
3917
3918 NOTE
3919 Will fail if <from_directory> doesn't exist.
3920 */
3921
3922 61 static void do_force_cpdir(struct st_command *command) {
3923
1/2
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
61 DBUG_TRACE;
3924
3925 static DYNAMIC_STRING ds_source;
3926 static DYNAMIC_STRING ds_destination;
3927
3928 61 const struct command_arg copy_file_args[] = {
3929 {"from_directory", ARG_STRING, true, &ds_source,
3930 "Directory to copy from"},
3931 {"to_directory", ARG_STRING, true, &ds_destination,
3932 "Directory to copy to"}};
3933
3934
1/2
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
61 check_command_args(command, command->first_argument, copy_file_args,
3935 sizeof(copy_file_args) / sizeof(struct command_arg), ' ');
3936
3937
3/8
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 61 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
61 DBUG_PRINT("info", ("Recursive copy files of %s to %s", ds_source.str,
3938 ds_destination.str));
3939
3940
3/8
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 61 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
61 DBUG_PRINT("info", ("listing directory: %s", ds_source.str));
3941
3942 61 int error = 0;
3943
3944 /*
3945 Throw an error if source directory path and
3946 destination directory path are same.
3947 */
3948
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 60 times.
61 if (!std::strcmp(ds_source.str, ds_destination.str)) {
3949 1 error = 1;
3950
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 set_my_errno(EEXIST);
3951 } else
3952
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
60 error = recursive_copy(&ds_source, &ds_destination);
3953
3954
1/2
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
61 handle_command_error(command, error);
3955
1/2
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
61 dynstr_free(&ds_source);
3956
1/2
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
61 dynstr_free(&ds_destination);
3957 61 }
3958
3959 /// Copy files from source directory to destination directory, by matching
3960 /// a specified file name pattern.
3961 ///
3962 /// Copy will fail if no files match the pattern. It will fail if source
3963 /// directory is empty and/or there are no files in it. Copy will also
3964 /// fail if source directory or destination directory or both do not
3965 /// exist. Retry of the command can happen optionally with an interval of
3966 /// one second between each retry if the command fails.
3967 ///
3968 /// @param command Pointer to the st_command structure which holds the
3969 /// arguments and information for the command.
3970 173 static void do_copy_files_wildcard(struct st_command *command) {
3971 static DYNAMIC_STRING ds_source;
3972 static DYNAMIC_STRING ds_destination;
3973 static DYNAMIC_STRING ds_wild;
3974 static DYNAMIC_STRING ds_retry;
3975
3976 173 const struct command_arg copy_file_args[] = {
3977 {"from_directory", ARG_STRING, true, &ds_source,
3978 "Directory to copy from"},
3979 {"to_directory", ARG_STRING, true, &ds_destination,
3980 "Directory to copy to"},
3981 {"pattern", ARG_STRING, true, &ds_wild, "File name pattern"},
3982 {"retry", ARG_STRING, false, &ds_retry, "Number of retries"}};
3983
1/2
✓ Branch 0 taken 173 times.
✗ Branch 1 not taken.
173 DBUG_TRACE;
3984
3985
1/2
✓ Branch 0 taken 172 times.
✗ Branch 1 not taken.
173 check_command_args(command, command->first_argument, copy_file_args,
3986 sizeof(copy_file_args) / sizeof(struct command_arg), ' ');
3987
3988
3/8
✓ Branch 0 taken 172 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 172 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 172 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
172 DBUG_PRINT("info",
3989 ("Copy files of %s to %s", ds_source.str, ds_destination.str));
3990
3991
3/8
✓ Branch 0 taken 172 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 172 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 172 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
172 DBUG_PRINT("info", ("listing directory: %s", ds_source.str));
3992
3993 172 int error = 0;
3994
3995 // Check if the retry value is passed, and if it is an integer
3996 172 int retry = 0;
3997
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 166 times.
172 if (ds_retry.length) {
3998
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 retry = get_int_val(ds_retry.str);
3999
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (retry < 0) {
4000 // In case of invalid retry, copy the value passed to print later
4001 char buf[32];
4002
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 strmake(buf, ds_retry.str, sizeof(buf) - 1);
4003
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 free_dynamic_strings(&ds_source, &ds_destination, &ds_wild, &ds_retry);
4004 5 die("Invalid value '%s' for retry argument given to "
4005 "copy_files_wildcard command.",
4006 buf);
4007 }
4008 }
4009
4010 /* Note that my_dir sorts the list if not given any flags */
4011
1/2
✓ Branch 0 taken 167 times.
✗ Branch 1 not taken.
167 MY_DIR *dir_info = my_dir(ds_source.str, MYF(MY_DONT_SORT | MY_WANT_STAT));
4012
4013 /* Directory does not exist or access denied */
4014
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 166 times.
167 if (!dir_info) {
4015 1 error = 1;
4016 1 goto end;
4017 }
4018
4019 /* The directory exists but is empty */
4020
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 165 times.
166 if (dir_info->number_off_files == 2) {
4021 1 error = 1;
4022
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 set_my_errno(ENOENT);
4023 1 goto end;
4024 }
4025
4026 char dir_separator[2];
4027 165 dir_separator[0] = FN_LIBCHAR;
4028 165 dir_separator[1] = 0;
4029
1/2
✓ Branch 0 taken 165 times.
✗ Branch 1 not taken.
165 dynstr_append(&ds_source, dir_separator);
4030
1/2
✓ Branch 0 taken 165 times.
✗ Branch 1 not taken.
165 dynstr_append(&ds_destination, dir_separator);
4031
4032 /* Storing the length of the path to the file, so it can be reused */
4033 size_t source_file_length;
4034 size_t dest_file_length;
4035 165 dest_file_length = ds_destination.length;
4036 165 source_file_length = ds_source.length;
4037 uint match_count;
4038 165 match_count = 0;
4039
4040
2/2
✓ Branch 0 taken 5447 times.
✓ Branch 1 taken 164 times.
5611 for (uint i = 0; i < dir_info->number_off_files; i++) {
4041 5447 ds_source.length = source_file_length;
4042 5447 ds_destination.length = dest_file_length;
4043 5447 FILEINFO *file = dir_info->dir_entry + i;
4044
4045 /*
4046 Copy only regular files, i.e. no directories etc.
4047 if (!MY_S_ISREG(file->mystat->st_mode))
4048 MY_S_ISREG does not work here on Windows, just skip directories
4049 */
4050
2/2
✓ Branch 0 taken 629 times.
✓ Branch 1 taken 4818 times.
5447 if (MY_S_ISDIR(file->mystat->st_mode)) continue;
4051
4052 /* Copy only those files which the pattern matches */
4053
2/2
✓ Branch 0 taken 3606 times.
✓ Branch 1 taken 1212 times.
4818 if (wild_compare_full(file->name, std::strlen(file->name), ds_wild.str,
4054
1/2
✓ Branch 0 taken 4818 times.
✗ Branch 1 not taken.
4818 std::strlen(ds_wild.str), false, 0, '?', '*'))
4055 3606 continue;
4056
4057 1212 match_count++;
4058
1/2
✓ Branch 0 taken 1212 times.
✗ Branch 1 not taken.
1212 dynstr_append(&ds_source, file->name);
4059
1/2
✓ Branch 0 taken 1212 times.
✗ Branch 1 not taken.
1212 dynstr_append(&ds_destination, file->name);
4060
3/8
✓ Branch 0 taken 1212 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1212 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1212 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1212 DBUG_PRINT("info",
4061 ("Copying file: %s to %s", ds_source.str, ds_destination.str));
4062
4063 /* MY_HOLD_ORIGINAL_MODES prevents attempts to chown the file */
4064
1/2
✓ Branch 0 taken 1212 times.
✗ Branch 1 not taken.
1212 error = (my_copy(ds_source.str, ds_destination.str,
4065 1212 MYF(MY_HOLD_ORIGINAL_MODES)) != 0);
4066
4067 /*
4068 If the copy command fails due to an environmental issue, the command can
4069 be retried a specified number of times before throwing an error.
4070 */
4071
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1211 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1212 for (int j = 0; error && (j < retry); j++) {
4072 my_sleep(1000 * 1000);
4073 error =
4074 (my_copy(ds_source.str, ds_destination.str,
4075 MYF(MY_DONT_OVERWRITE_FILE | MY_HOLD_ORIGINAL_MODES)) != 0);
4076 }
4077
4078
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1211 times.
1212 if (error) goto end;
4079 }
4080
4081 /* Pattern did not match any files */
4082
2/2
✓ Branch 0 taken 163 times.
✓ Branch 1 taken 1 times.
164 if (!match_count) {
4083 1 error = 1;
4084
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 set_my_errno(ENOENT);
4085 }
4086
4087 163 end:
4088
1/2
✓ Branch 0 taken 167 times.
✗ Branch 1 not taken.
167 my_dirend(dir_info);
4089
1/2
✓ Branch 0 taken 167 times.
✗ Branch 1 not taken.
167 handle_command_error(command, error);
4090
1/2
✓ Branch 0 taken 167 times.
✗ Branch 1 not taken.
167 free_dynamic_strings(&ds_source, &ds_destination, &ds_wild, &ds_retry);
4091 167 }
4092
4093 /*
4094 SYNOPSIS
4095 move_file_by_copy_delete
4096 from path of source
4097 to path of destination
4098
4099 DESCRIPTION
4100 Move <from_file> to <to_file>
4101 Auxiliary function for copying <from_file> to <to_file> followed by
4102 deleting <to_file>.
4103 */
4104
4105 static int move_file_by_copy_delete(const char *from, const char *to) {
4106 int error_copy, error_delete;
4107 error_copy = (my_copy(from, to, MYF(MY_HOLD_ORIGINAL_MODES)) != 0);
4108 if (error_copy) {
4109 return error_copy;
4110 }
4111
4112 error_delete = my_delete(from, MYF(0)) != 0;
4113
4114 /*
4115 If deleting the source file fails, rollback by deleting the
4116 redundant copy at the destinatiion.
4117 */
4118 if (error_delete) {
4119 my_delete(to, MYF(0));
4120 }
4121 return error_delete;
4122 }
4123
4124 /// Moves a file to destination file. Retry of the command can happen
4125 /// optionally with an interval of one second between each retry if
4126 /// the command fails.
4127 ///
4128 /// @param command Pointer to the st_command structure which holds the
4129 /// arguments and information for the command.
4130 263 static void do_move_file(struct st_command *command) {
4131 int error;
4132 static DYNAMIC_STRING ds_from_file;
4133 static DYNAMIC_STRING ds_to_file;
4134 static DYNAMIC_STRING ds_retry;
4135
4136 263 const struct command_arg move_file_args[] = {
4137 {"from_file", ARG_STRING, true, &ds_from_file, "Filename to move from"},
4138 {"to_file", ARG_STRING, true, &ds_to_file, "Filename to move to"},
4139 {"retry", ARG_STRING, false, &ds_retry, "Number of retries"}};
4140
1/2
✓ Branch 0 taken 263 times.
✗ Branch 1 not taken.
263 DBUG_TRACE;
4141
4142
1/2
✓ Branch 0 taken 261 times.
✗ Branch 1 not taken.
263 check_command_args(command, command->first_argument, move_file_args,
4143 sizeof(move_file_args) / sizeof(struct command_arg), ' ');
4144
4145 // Check if the retry value is passed, and if it is an integer
4146 261 int retry = 0;
4147
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 255 times.
261 if (ds_retry.length) {
4148
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 retry = get_int_val(ds_retry.str);
4149
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (retry < 0) {
4150 // In case of invalid retry, copy the value passed to print later
4151 char buf[32];
4152
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 strmake(buf, ds_retry.str, sizeof(buf) - 1);
4153
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 free_dynamic_strings(&ds_from_file, &ds_to_file, &ds_retry);
4154 5 die("Invalid value '%s' for retry argument given to move_file "
4155 "command.",
4156 buf);
4157 }
4158 }
4159
4160
3/8
✓ Branch 0 taken 256 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 256 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 256 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
256 DBUG_PRINT("info", ("Move %s to %s", ds_from_file.str, ds_to_file.str));
4161
1/2
✓ Branch 0 taken 256 times.
✗ Branch 1 not taken.
256 error = (my_rename(ds_from_file.str, ds_to_file.str, MYF(0)) != 0);
4162
4163 /*
4164 Use my_copy() followed by my_delete() for moving a file instead of
4165 my_rename() when my_errno is EXDEV. This is because my_rename() fails
4166 with the error "Invalid cross-device link" while moving a file between
4167 locations having different filesystems in some operating systems.
4168 */
4169
5/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 255 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 256 times.
256 if (error && (my_errno() == EXDEV)) {
4170 error = move_file_by_copy_delete(ds_from_file.str, ds_to_file.str);
4171 }
4172
4173 /*
4174 If the command fails due to an environmental issue, the command can be
4175 retried a specified number of times before throwing an error.
4176 */
4177
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
256 for (int i = 0; error && (i < retry); i++) {
4178 my_sleep(1000 * 1000);
4179 error = (my_rename(ds_from_file.str, ds_to_file.str, MYF(0)) != 0);
4180
4181 if (error && (my_errno() == EXDEV))
4182 error = move_file_by_copy_delete(ds_from_file.str, ds_to_file.str);
4183 }
4184
4185
1/2
✓ Branch 0 taken 256 times.
✗ Branch 1 not taken.
256 handle_command_error(command, error);
4186
1/2
✓ Branch 0 taken 256 times.
✗ Branch 1 not taken.
256 free_dynamic_strings(&ds_from_file, &ds_to_file, &ds_retry);
4187 256 }
4188
4189 /*
4190 SYNOPSIS
4191 do_chmod_file
4192 command command handle
4193
4194 DESCRIPTION
4195 chmod <octal> <file_name>
4196 Change file permission of <file_name>
4197
4198 */
4199
4200 366 static void do_chmod_file(struct st_command *command) {
4201 366 long mode = 0;
4202 int err_code;
4203 static DYNAMIC_STRING ds_mode;
4204 static DYNAMIC_STRING ds_file;
4205 366 const struct command_arg chmod_file_args[] = {
4206 {"mode", ARG_STRING, true, &ds_mode, "Mode of file(octal) ex. 0660"},
4207 {"filename", ARG_STRING, true, &ds_file, "Filename of file to modify"}};
4208
1/2
✓ Branch 0 taken 366 times.
✗ Branch 1 not taken.
366 DBUG_TRACE;
4209
4210
1/2
✓ Branch 0 taken 364 times.
✗ Branch 1 not taken.
366 check_command_args(command, command->first_argument, chmod_file_args,
4211 sizeof(chmod_file_args) / sizeof(struct command_arg), ' ');
4212
4213 /* Parse what mode to set */
4214
4/4
✓ Branch 0 taken 361 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 360 times.
725 if (ds_mode.length != 4 ||
4215
3/4
✓ Branch 0 taken 361 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 360 times.
361 str2int(ds_mode.str, 8, 0, INT_MAX, &mode) == NullS)
4216 4 die("You must write a 4 digit octal number for mode");
4217
4218
3/8
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 360 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 360 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
360 DBUG_PRINT("info", ("chmod %o %s", (uint)mode, ds_file.str));
4219 360 err_code = chmod(ds_file.str, mode);
4220
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 360 times.
360 if (err_code < 0) err_code = 1;
4221
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 handle_command_error(command, err_code);
4222
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 dynstr_free(&ds_mode);
4223
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 dynstr_free(&ds_file);
4224 360 }
4225
4226 /// Check if specified file exists. Retry of the command can happen
4227 /// optionally with an interval of one second between each retry if
4228 /// the command fails.
4229 ///
4230 /// @param command Pointer to the st_command structure which holds the
4231 /// arguments and information for the command.
4232 805 static void do_file_exist(struct st_command *command) {
4233 int error;
4234 static DYNAMIC_STRING ds_filename;
4235 static DYNAMIC_STRING ds_retry;
4236
4237 805 const struct command_arg file_exist_args[] = {
4238 {"filename", ARG_STRING, true, &ds_filename, "File to check if it exist"},
4239 {"retry", ARG_STRING, false, &ds_retry, "Number of retries"}};
4240
1/2
✓ Branch 0 taken 805 times.
✗ Branch 1 not taken.
805 DBUG_TRACE;
4241
4242
1/2
✓ Branch 0 taken 804 times.
✗ Branch 1 not taken.
805 check_command_args(command, command->first_argument, file_exist_args,
4243 sizeof(file_exist_args) / sizeof(struct command_arg), ' ');
4244
4245 // Check if the retry value is passed, and if it is an integer
4246 804 int retry = 0;
4247
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 798 times.
804 if (ds_retry.length) {
4248
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 retry = get_int_val(ds_retry.str);
4249
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (retry < 0) {
4250 // In case of invalid retry, copy the value passed to print later
4251 char buf[32];
4252
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 strmake(buf, ds_retry.str, sizeof(buf) - 1);
4253
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 free_dynamic_strings(&ds_filename, &ds_retry);
4254 5 die("Invalid value '%s' for retry argument given to file_exists "
4255 "command.",
4256 buf);
4257 }
4258 }
4259
4260
3/8
✓ Branch 0 taken 799 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 799 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 799 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
799 DBUG_PRINT("info", ("Checking for existence of file: %s", ds_filename.str));
4261 799 error = (access(ds_filename.str, F_OK) != 0);
4262
4263 /*
4264 If the file_exists command fails due to an environmental issue, the command
4265 can be retried a specified number of times before throwing an error.
4266 */
4267
3/4
✓ Branch 0 taken 166 times.
✓ Branch 1 taken 633 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 166 times.
799 for (int i = 0; error && (i < retry); i++) {
4268 my_sleep(1000 * 1000);
4269 error = (access(ds_filename.str, F_OK) != 0);
4270 }
4271
4272
1/2
✓ Branch 0 taken 799 times.
✗ Branch 1 not taken.
799 handle_command_error(command, error);
4273
1/2
✓ Branch 0 taken 799 times.
✗ Branch 1 not taken.
799 free_dynamic_strings(&ds_filename, &ds_retry);
4274 799 }
4275
4276 /*
4277 SYNOPSIS
4278 do_mkdir
4279 command called command
4280
4281 DESCRIPTION
4282 mkdir <dir_name>
4283 Create the directory <dir_name>
4284 */
4285
4286 360 static void do_mkdir(struct st_command *command) {
4287 int error;
4288 static DYNAMIC_STRING ds_dirname;
4289 360 const struct command_arg mkdir_args[] = {
4290 {"dirname", ARG_STRING, true, &ds_dirname, "Directory to create"}};
4291
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 DBUG_TRACE;
4292
4293
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 check_command_args(command, command->first_argument, mkdir_args,
4294 sizeof(mkdir_args) / sizeof(struct command_arg), ' ');
4295
4296
3/8
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 360 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 360 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
360 DBUG_PRINT("info", ("creating directory: %s", ds_dirname.str));
4297
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 error = my_mkdir(ds_dirname.str, 0777, MYF(0)) != 0;
4298
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 handle_command_error(command, error);
4299
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 dynstr_free(&ds_dirname);
4300 360 }
4301
4302 /*
4303 SYNOPSIS
4304 do_force_rmdir
4305 command - command handle
4306 ds_dirname - pointer to dynamic string containing directory information
4307
4308 DESCRIPTION
4309 force-rmdir <dir_name>
4310 Remove the directory <dir_name>
4311 */
4312
4313 4480 void do_force_rmdir(struct st_command *command, DYNAMIC_STRING *ds_dirname) {
4314
1/2
✓ Branch 0 taken 4480 times.
✗ Branch 1 not taken.
4480 DBUG_TRACE;
4315
4316 char dir_name[FN_REFLEN + 1];
4317 4480 strncpy(dir_name, ds_dirname->str, sizeof(dir_name) - 1);
4318 4480 dir_name[FN_REFLEN] = '\0';
4319
4320 /* Note that my_dir sorts the list if not given any flags */
4321
1/2
✓ Branch 0 taken 4480 times.
✗ Branch 1 not taken.
4480 MY_DIR *dir_info = my_dir(ds_dirname->str, MYF(MY_DONT_SORT | MY_WANT_STAT));
4322
4323
4/4
✓ Branch 0 taken 4448 times.
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 3703 times.
✓ Branch 3 taken 745 times.
4480 if (dir_info && dir_info->number_off_files > 2) {
4324 /* Storing the length of the path to the file, so it can be reused */
4325 3703 size_t length = ds_dirname->length;
4326
4327 /* Delete the directory recursively */
4328
2/2
✓ Branch 0 taken 82590 times.
✓ Branch 1 taken 3703 times.
86293 for (uint i = 0; i < dir_info->number_off_files; i++) {
4329 82590 FILEINFO *file = dir_info->dir_entry + i;
4330
4331 /* Skip the names "." and ".." */
4332
4/4
✓ Branch 0 taken 78887 times.
✓ Branch 1 taken 3703 times.
✓ Branch 2 taken 3703 times.
✓ Branch 3 taken 75184 times.
82590 if (!std::strcmp(file->name, ".") || !std::strcmp(file->name, ".."))
4333 7406 continue;
4334
4335 75184 ds_dirname->length = length;
4336 75184 char dir_separator[2] = {FN_LIBCHAR, 0};
4337
1/2
✓ Branch 0 taken 75184 times.
✗ Branch 1 not taken.
75184 dynstr_append(ds_dirname, dir_separator);
4338
1/2
✓ Branch 0 taken 75184 times.
✗ Branch 1 not taken.
75184 dynstr_append(ds_dirname, file->name);
4339
4340
2/2
✓ Branch 0 taken 3758 times.
✓ Branch 1 taken 71426 times.
75184 if (MY_S_ISDIR(file->mystat->st_mode)) /* It's a directory */
4341
1/2
✓ Branch 0 taken 3758 times.
✗ Branch 1 not taken.
3758 do_force_rmdir(command, ds_dirname);
4342 else
4343 /* It's a file */
4344
1/2
✓ Branch 0 taken 71426 times.
✗ Branch 1 not taken.
71426 my_delete(ds_dirname->str, MYF(0));
4345 }
4346 }
4347
4348
1/2
✓ Branch 0 taken 4480 times.
✗ Branch 1 not taken.
4480 my_dirend(dir_info);
4349 4480 int error = rmdir(dir_name) != 0;
4350
1/2
✓ Branch 0 taken 4480 times.
✗ Branch 1 not taken.
4480 set_my_errno(errno);
4351
1/2
✓ Branch 0 taken 4480 times.
✗ Branch 1 not taken.
4480 handle_command_error(command, error);
4352 4480 }
4353
4354 /*
4355 SYNOPSIS
4356 do_rmdir
4357 command called command
4358 force Recursively delete a directory if the value is set to true,
4359 otherwise delete an empty directory
4360
4361 DESCRIPTION
4362 rmdir <dir_name>
4363 Remove the empty directory <dir_name>
4364 */
4365
4366 938 static void do_rmdir(struct st_command *command, bool force) {
4367 int error;
4368 static DYNAMIC_STRING ds_dirname;
4369 938 const struct command_arg rmdir_args[] = {
4370 {"dirname", ARG_STRING, true, &ds_dirname, "Directory to remove"}};
4371
1/2
✓ Branch 0 taken 938 times.
✗ Branch 1 not taken.
938 DBUG_TRACE;
4372
4373
1/2
✓ Branch 0 taken 938 times.
✗ Branch 1 not taken.
938 check_command_args(command, command->first_argument, rmdir_args,
4374 sizeof(rmdir_args) / sizeof(struct command_arg), ' ');
4375
4376
3/8
✓ Branch 0 taken 938 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 938 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 938 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
938 DBUG_PRINT("info", ("removing directory: %s", ds_dirname.str));
4377
2/2
✓ Branch 0 taken 722 times.
✓ Branch 1 taken 216 times.
938 if (force)
4378
1/2
✓ Branch 0 taken 722 times.
✗ Branch 1 not taken.
722 do_force_rmdir(command, &ds_dirname);
4379 else {
4380 216 error = rmdir(ds_dirname.str) != 0;
4381
1/2
✓ Branch 0 taken 216 times.
✗ Branch 1 not taken.
216 set_my_errno(errno);
4382
1/2
✓ Branch 0 taken 216 times.
✗ Branch 1 not taken.
216 handle_command_error(command, error);
4383 }
4384
1/2
✓ Branch 0 taken 938 times.
✗ Branch 1 not taken.
938 dynstr_free(&ds_dirname);
4385 938 }
4386
4387 /*
4388 SYNOPSIS
4389 get_list_files
4390 ds output
4391 ds_dirname dir to list
4392 ds_wild wild-card file pattern (can be empty)
4393
4394 DESCRIPTION
4395 list all entries in directory (matching ds_wild if given)
4396 */
4397
4398 148545 static int get_list_files(DYNAMIC_STRING *ds, const DYNAMIC_STRING *ds_dirname,
4399 const DYNAMIC_STRING *ds_wild) {
4400 uint i;
4401 MY_DIR *dir_info;
4402 FILEINFO *file;
4403
1/2
✓ Branch 0 taken 148545 times.
✗ Branch 1 not taken.
148545 DBUG_TRACE;
4404
4405
3/8
✓ Branch 0 taken 148545 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 148545 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 148545 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
148545 DBUG_PRINT("info", ("listing directory: %s", ds_dirname->str));
4406 /* Note that my_dir sorts the list if not given any flags */
4407
2/4
✓ Branch 0 taken 148545 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 148545 times.
148545 if (!(dir_info = my_dir(ds_dirname->str, MYF(0)))) return 1;
4408
2/2
✓ Branch 0 taken 8615183 times.
✓ Branch 1 taken 148545 times.
8763728 for (i = 0; i < (uint)dir_info->number_off_files; i++) {
4409 8615183 file = dir_info->dir_entry + i;
4410
2/2
✓ Branch 0 taken 326586 times.
✓ Branch 1 taken 8288597 times.
8615183 if (file->name[0] == '.' &&
4411
2/2
✓ Branch 0 taken 178041 times.
✓ Branch 1 taken 148545 times.
326586 (file->name[1] == '\0' ||
4412
3/4
✓ Branch 0 taken 148545 times.
✓ Branch 1 taken 29496 times.
✓ Branch 2 taken 148545 times.
✗ Branch 3 not taken.
178041 (file->name[1] == '.' && file->name[2] == '\0')))
4413 297090 continue; /* . or .. */
4414
5/6
✓ Branch 0 taken 8318093 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8314672 times.
✓ Branch 3 taken 3421 times.
✓ Branch 4 taken 4706 times.
✓ Branch 5 taken 8313387 times.
16632765 if (ds_wild && ds_wild->length &&
4415
2/2
✓ Branch 0 taken 4706 times.
✓ Branch 1 taken 8309966 times.
8314672 wild_compare_full(file->name, std::strlen(file->name), ds_wild->str,
4416
1/2
✓ Branch 0 taken 8314672 times.
✗ Branch 1 not taken.
8314672 std::strlen(ds_wild->str), false, 0, '?', '*'))
4417 4706 continue;
4418
1/2
✓ Branch 0 taken 8313387 times.
✗ Branch 1 not taken.
8313387 replace_dynstr_append(ds, file->name);
4419
1/2
✓ Branch 0 taken 8313387 times.
✗ Branch 1 not taken.
8313387 dynstr_append(ds, "\n");
4420 }
4421
1/2
✓ Branch 0 taken 148545 times.
✗ Branch 1 not taken.
148545 my_dirend(dir_info);
4422 148545 return 0;
4423 148545 }
4424
4425 /*
4426 SYNOPSIS
4427 do_list_files
4428 command called command
4429
4430 DESCRIPTION
4431 list_files <dir_name> [<file_name>]
4432 List files and directories in directory <dir_name> (like `ls`)
4433 [Matching <file_name>, where wild-cards are allowed]
4434 */
4435
4436 765 static void do_list_files(struct st_command *command) {
4437 int error;
4438 static DYNAMIC_STRING ds_dirname;
4439 static DYNAMIC_STRING ds_wild;
4440 765 const struct command_arg list_files_args[] = {
4441 {"dirname", ARG_STRING, true, &ds_dirname, "Directory to list"},
4442 {"file", ARG_STRING, false, &ds_wild, "Filename (incl. wildcard)"}};
4443
1/2
✓ Branch 0 taken 765 times.
✗ Branch 1 not taken.
765 DBUG_TRACE;
4444 765 command->used_replace = true;
4445
4446
1/2
✓ Branch 0 taken 765 times.
✗ Branch 1 not taken.
765 check_command_args(command, command->first_argument, list_files_args,
4447 sizeof(list_files_args) / sizeof(struct command_arg), ' ');
4448
4449
1/2
✓ Branch 0 taken 765 times.
✗ Branch 1 not taken.
765 error = get_list_files(&ds_res, &ds_dirname, &ds_wild);
4450
1/2
✓ Branch 0 taken 765 times.
✗ Branch 1 not taken.
765 handle_command_error(command, error);
4451
1/2
✓ Branch 0 taken 765 times.
✗ Branch 1 not taken.
765 dynstr_free(&ds_dirname);
4452
1/2
✓ Branch 0 taken 765 times.
✗ Branch 1 not taken.
765 dynstr_free(&ds_wild);
4453 765 }
4454
4455 /*
4456 SYNOPSIS
4457 do_list_files_write_file_command
4458 command called command
4459 append append file, or create new
4460
4461 DESCRIPTION
4462 list_files_{write|append}_file <filename> <dir_name> [<match_file>]
4463 List files and directories in directory <dir_name> (like `ls`)
4464 [Matching <match_file>, where wild-cards are allowed]
4465
4466 Note: File will be truncated if exists and append is not true.
4467 */
4468
4469 147780 static void do_list_files_write_file_command(struct st_command *command,
4470 bool append) {
4471 int error;
4472 static DYNAMIC_STRING ds_content;
4473 static DYNAMIC_STRING ds_filename;
4474 static DYNAMIC_STRING ds_dirname;
4475 static DYNAMIC_STRING ds_wild;
4476 147780 const struct command_arg list_files_args[] = {
4477 {"filename", ARG_STRING, true, &ds_filename, "Filename for write"},
4478 {"dirname", ARG_STRING, true, &ds_dirname, "Directory to list"},
4479 {"file", ARG_STRING, false, &ds_wild, "Filename (incl. wildcard)"}};
4480
1/2
✓ Branch 0 taken 147780 times.
✗ Branch 1 not taken.
147780 DBUG_TRACE;
4481 147780 command->used_replace = true;
4482
4483
1/2
✓ Branch 0 taken 147780 times.
✗ Branch 1 not taken.
147780 check_command_args(command, command->first_argument, list_files_args,
4484 sizeof(list_files_args) / sizeof(struct command_arg), ' ');
4485
4486
1/2
✓ Branch 0 taken 147780 times.
✗ Branch 1 not taken.
147780 init_dynamic_string(&ds_content, "", 1024);
4487
1/2
✓ Branch 0 taken 147780 times.
✗ Branch 1 not taken.
147780 error = get_list_files(&ds_content, &ds_dirname, &ds_wild);
4488
1/2
✓ Branch 0 taken 147780 times.
✗ Branch 1 not taken.
147780 handle_command_error(command, error);
4489
1/2
✓ Branch 0 taken 147780 times.
✗ Branch 1 not taken.
147780 str_to_file2(ds_filename.str, ds_content.str, ds_content.length, append);
4490
1/2
✓ Branch 0 taken 147780 times.
✗ Branch 1 not taken.
147780 dynstr_free(&ds_content);
4491
1/2
✓ Branch 0 taken 147780 times.
✗ Branch 1 not taken.
147780 dynstr_free(&ds_filename);
4492
1/2
✓ Branch 0 taken 147780 times.
✗ Branch 1 not taken.
147780 dynstr_free(&ds_dirname);
4493
1/2
✓ Branch 0 taken 147780 times.
✗ Branch 1 not taken.
147780 dynstr_free(&ds_wild);
4494 147780 }
4495
4496 /*
4497 Read characters from line buffer or file. This is needed to allow
4498 my_ungetc() to buffer MAX_DELIMITER_LENGTH characters for a file
4499
4500 NOTE:
4501 This works as long as one doesn't change files (with 'source file_name')
4502 when there is things pushed into the buffer. This should however not
4503 happen for any tests in the test suite.
4504 */
4505
4506 16379951920 static int my_getc(FILE *file) {
4507
2/2
✓ Branch 0 taken 16375375997 times.
✓ Branch 1 taken 4575923 times.
16379951920 if (line_buffer_pos == line_buffer) return fgetc(file);
4508 4575923 return *--line_buffer_pos;
4509 }
4510
4511 4575923 static void my_ungetc(int c) { *line_buffer_pos++ = (char)c; }
4512
4513 481609 static void read_until_delimiter(DYNAMIC_STRING *ds,
4514 DYNAMIC_STRING *ds_delimiter) {
4515 char c;
4516
1/2
✓ Branch 0 taken 481609 times.
✗ Branch 1 not taken.
481609 DBUG_TRACE;
4517
3/8
✓ Branch 0 taken 481609 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 481609 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 481609 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
481609 DBUG_PRINT("enter", ("delimiter: %s, length: %u", ds_delimiter->str,
4518 (uint)ds_delimiter->length));
4519
4520
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 481608 times.
481609 if (ds_delimiter->length > MAX_DELIMITER_LENGTH)
4521 1 die("Max delimiter length(%d) exceeded", MAX_DELIMITER_LENGTH);
4522
4523 /* Read from file until delimiter is found */
4524 while (true) {
4525
1/2
✓ Branch 0 taken 217668368 times.
✗ Branch 1 not taken.
217668368 c = my_getc(cur_file->file);
4526
4527
2/2
✓ Branch 0 taken 6875145 times.
✓ Branch 1 taken 210793223 times.
217668368 if (c == '\n') {
4528 6875145 cur_file->lineno++;
4529
4530 /* Skip newline from the same line as the command */
4531
2/2
✓ Branch 0 taken 281597 times.
✓ Branch 1 taken 6593548 times.
6875145 if (start_lineno == (cur_file->lineno - 1)) continue;
4532
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 210793223 times.
210793223 } else if (start_lineno == cur_file->lineno) {
4533 /*
4534 No characters except \n are allowed on
4535 the same line as the command
4536 */
4537 die("Trailing characters found after command");
4538 }
4539
4540
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 217386770 times.
217386771 if (feof(cur_file->file))
4541 1 die("End of file encountered before '%s' delimiter was found",
4542 ds_delimiter->str);
4543
4544
3/4
✓ Branch 0 taken 217386770 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 481607 times.
✓ Branch 3 taken 216905163 times.
217386770 if (match_delimiter(c, ds_delimiter->str, ds_delimiter->length)) {
4545
3/8
✓ Branch 0 taken 481607 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 481607 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 481607 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
481607 DBUG_PRINT("exit", ("Found delimiter '%s'", ds_delimiter->str));
4546 481607 break;
4547 }
4548
1/2
✓ Branch 0 taken 216905163 times.
✗ Branch 1 not taken.
216905163 dynstr_append_mem(ds, (const char *)&c, 1);
4549 }
4550
3/8
✓ Branch 0 taken 481607 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 481607 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 481607 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
481607 DBUG_PRINT("exit", ("ds: %s", ds->str));
4551 481607 }
4552
4553 191402 static void do_write_file_command(struct st_command *command, bool append) {
4554 static DYNAMIC_STRING ds_content;
4555 static DYNAMIC_STRING ds_filename;
4556 static DYNAMIC_STRING ds_delimiter;
4557 191402 const struct command_arg write_file_args[] = {
4558 {"filename", ARG_STRING, true, &ds_filename, "File to write to"},
4559 {"delimiter", ARG_STRING, false, &ds_delimiter,
4560 "Delimiter to read until"}};
4561
1/2
✓ Branch 0 taken 191402 times.
✗ Branch 1 not taken.
191402 DBUG_TRACE;
4562
4563
1/2
✓ Branch 0 taken 191401 times.
✗ Branch 1 not taken.
191402 check_command_args(command, command->first_argument, write_file_args,
4564 sizeof(write_file_args) / sizeof(struct command_arg), ' ');
4565
4566
6/6
✓ Branch 0 taken 30750 times.
✓ Branch 1 taken 160651 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 30749 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 191400 times.
191401 if (!append && access(ds_filename.str, F_OK) == 0) {
4567 /* The file should not be overwritten */
4568 1 die("File already exist: '%s'", ds_filename.str);
4569 }
4570
4571 191400 ds_content = command->content;
4572 /* If it hasn't been done already by a loop iteration, fill it in */
4573
2/2
✓ Branch 0 taken 184908 times.
✓ Branch 1 taken 6492 times.
191400 if (!ds_content.str) {
4574 /* If no delimiter was provided, use EOF */
4575
3/4
✓ Branch 0 taken 184752 times.
✓ Branch 1 taken 156 times.
✓ Branch 2 taken 184752 times.
✗ Branch 3 not taken.
184908 if (ds_delimiter.length == 0) dynstr_set(&ds_delimiter, "EOF");
4576
4577
1/2
✓ Branch 0 taken 184908 times.
✗ Branch 1 not taken.
184908 init_dynamic_string(&ds_content, "", 1024);
4578
1/2
✓ Branch 0 taken 184907 times.
✗ Branch 1 not taken.
184908 read_until_delimiter(&ds_content, &ds_delimiter);
4579 184907 command->content = ds_content;
4580 }
4581 /* This function could be called even if "false", so check before printing */
4582
2/2
✓ Branch 0 taken 181346 times.
✓ Branch 1 taken 10053 times.
191399 if (cur_block->ok) {
4583
3/8
✓ Branch 0 taken 181346 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 181346 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 181346 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
181346 DBUG_PRINT("info", ("Writing to file: %s", ds_filename.str));
4584
1/2
✓ Branch 0 taken 181346 times.
✗ Branch 1 not taken.
181346 str_to_file2(ds_filename.str, ds_content.str, ds_content.length, append);
4585 }
4586
1/2
✓ Branch 0 taken 191399 times.
✗ Branch 1 not taken.
191399 dynstr_free(&ds_filename);
4587
1/2
✓ Branch 0 taken 191399 times.
✗ Branch 1 not taken.
191399 dynstr_free(&ds_delimiter);
4588 191399 }
4589
4590 /*
4591 SYNOPSIS
4592 do_write_file
4593 command called command
4594
4595 DESCRIPTION
4596 write_file <file_name> [<delimiter>];
4597 <what to write line 1>
4598 <...>
4599 < what to write line n>
4600 EOF
4601
4602 --write_file <file_name>;
4603 <what to write line 1>
4604 <...>
4605 < what to write line n>
4606 EOF
4607
4608 Write everything between the "write_file" command and 'delimiter'
4609 to "file_name"
4610
4611 NOTE! Will fail if <file_name> exists
4612
4613 Default <delimiter> is EOF
4614
4615 */
4616
4617 30751 static void do_write_file(struct st_command *command) {
4618 30751 do_write_file_command(command, false);
4619 30748 }
4620
4621 /*
4622 SYNOPSIS
4623 do_append_file
4624 command called command
4625
4626 DESCRIPTION
4627 append_file <file_name> [<delimiter>];
4628 <what to write line 1>
4629 <...>
4630 < what to write line n>
4631 EOF
4632
4633 --append_file <file_name>;
4634 <what to write line 1>
4635 <...>
4636 < what to write line n>
4637 EOF
4638
4639 Append everything between the "append_file" command
4640 and 'delimiter' to "file_name"
4641
4642 Default <delimiter> is EOF
4643
4644 */
4645
4646 160651 static void do_append_file(struct st_command *command) {
4647 160651 do_write_file_command(command, true);
4648 160651 }
4649
4650 /*
4651 SYNOPSIS
4652 do_cat_file
4653 command called command
4654
4655 DESCRIPTION
4656 cat_file <file_name>;
4657
4658 Print the given file to result log
4659
4660 */
4661
4662 13118 static void do_cat_file(struct st_command *command) {
4663 int error;
4664 static DYNAMIC_STRING ds_filename;
4665 13118 const struct command_arg cat_file_args[] = {
4666 {"filename", ARG_STRING, true, &ds_filename, "File to read from"}};
4667
1/2
✓ Branch 0 taken 13118 times.
✗ Branch 1 not taken.
13118 DBUG_TRACE;
4668 13118 command->used_replace = true;
4669
4670
1/2
✓ Branch 0 taken 13118 times.
✗ Branch 1 not taken.
13118 check_command_args(command, command->first_argument, cat_file_args,
4671 sizeof(cat_file_args) / sizeof(struct command_arg), ' ');
4672
4673
3/8
✓ Branch 0 taken 13118 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13118 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 13118 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
13118 DBUG_PRINT("info", ("Reading from, file: %s", ds_filename.str));
4674
4675
1/2
✓ Branch 0 taken 13118 times.
✗ Branch 1 not taken.
13118 error = cat_file(&ds_res, ds_filename.str);
4676
1/2
✓ Branch 0 taken 13117 times.
✗ Branch 1 not taken.
13118 handle_command_error(command, error);
4677
1/2
✓ Branch 0 taken 13117 times.
✗ Branch 1 not taken.
13117 dynstr_free(&ds_filename);
4678 13117 }
4679
4680 /// Compare the two files.
4681 ///
4682 /// Success if 2 files are same, failure if the files are different or
4683 /// either file does not exist.
4684 ///
4685 /// @code
4686 /// diff_files <file1> <file2>;
4687 /// @endcode
4688 ///
4689 /// @param command Pointer to the st_command structure which holds the
4690 /// arguments and information for the command.
4691 16881 static void do_diff_files(struct st_command *command) {
4692 static DYNAMIC_STRING ds_filename1;
4693 static DYNAMIC_STRING ds_filename2;
4694
4695 16881 const struct command_arg diff_file_args[] = {
4696 {"file1", ARG_STRING, true, &ds_filename1, "First file to diff"},
4697 {"file2", ARG_STRING, true, &ds_filename2, "Second file to diff"}};
4698
4699
1/2
✓ Branch 0 taken 16881 times.
✗ Branch 1 not taken.
16881 check_command_args(command, command->first_argument, diff_file_args,
4700 sizeof(diff_file_args) / sizeof(struct command_arg), ' ');
4701
4702
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16881 times.
16881 if (access(ds_filename1.str, F_OK) != 0)
4703 die("Command \"diff_files\" failed, file '%s' does not exist.",
4704 ds_filename1.str);
4705
4706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16881 times.
16881 if (access(ds_filename2.str, F_OK) != 0)
4707 die("Command \"diff_files\" failed, file '%s' does not exist.",
4708 ds_filename2.str);
4709
4710 16881 int error = 0;
4711
4/6
✓ Branch 0 taken 16881 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 16877 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 16881 times.
16885 if ((error = compare_files(ds_filename1.str, ds_filename2.str)) &&
4712
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 match_expected_error(command, error, nullptr) < 0) {
4713 // Compare of the two files failed, append them to output so the
4714 // failure can be analyzed, but only if it was not expected to fail.
4715 show_diff(&ds_res, ds_filename1.str, ds_filename2.str);
4716 if (log_file.write(ds_res.str, ds_res.length) || log_file.flush())
4717 cleanup_and_exit(1);
4718 dynstr_set(&ds_res, nullptr);
4719 }
4720
4721
1/2
✓ Branch 0 taken 16881 times.
✗ Branch 1 not taken.
16881 free_dynamic_strings(&ds_filename1, &ds_filename2);
4722
1/2
✓ Branch 0 taken 16881 times.
✗ Branch 1 not taken.
16881 handle_command_error(command, error);
4723 16881 }
4724
4725 1206833 static struct st_connection *find_connection_by_name(const char *name) {
4726 struct st_connection *con;
4727
2/2
✓ Branch 0 taken 16616013 times.
✓ Branch 1 taken 47121 times.
16663134 for (con = connections; con < next_con; con++) {
4728
2/2
✓ Branch 0 taken 1159712 times.
✓ Branch 1 taken 15456301 times.
16616013 if (!std::strcmp(con->name, name)) {
4729 1159712 return con;
4730 }
4731 }
4732 47121 return nullptr; /* Connection not found */
4733 }
4734
4735 /*
4736 SYNOPSIS
4737 do_send_quit
4738 command called command
4739
4740 DESCRIPTION
4741 Sends a simple quit command to the server for the named connection.
4742
4743 */
4744
4745 2000 static void do_send_quit(struct st_command *command) {
4746 2000 char *p = command->first_argument;
4747 const char *name;
4748 struct st_connection *con;
4749
4750
1/2
✓ Branch 0 taken 2000 times.
✗ Branch 1 not taken.
2000 DBUG_TRACE;
4751
3/8
✓ Branch 0 taken 2000 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2000 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2000 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
2000 DBUG_PRINT("enter", ("name: '%s'", p));
4752
4753
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2000 times.
2000 if (!*p) die("Missing connection name in send_quit");
4754 2000 name = p;
4755
3/4
✓ Branch 0 taken 14000 times.
✓ Branch 1 taken 2000 times.
✓ Branch 2 taken 14000 times.
✗ Branch 3 not taken.
16000 while (*p && !my_isspace(charset_info, *p)) p++;
4756
4757
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2000 times.
2000 if (*p) *p++ = 0;
4758 2000 command->last_argument = p;
4759
4760
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2000 times.
2000 if (!(con = find_connection_by_name(name)))
4761 die("connection '%s' not found in connection pool", name);
4762
4763
2/6
✓ Branch 0 taken 2000 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2000 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2000 simple_command(&con->mysql, COM_QUIT, nullptr, 0, 1);
4764 2000 }
4765
4766 /*
4767 SYNOPSIS
4768 do_change_user
4769 command called command
4770
4771 DESCRIPTION
4772 change_user [<user>], [<passwd>], [<db>]
4773 <user> - user to change to
4774 <passwd> - user password
4775 <db> - default database
4776
4777 Changes the user and causes the database specified by db to become
4778 the default (current) database for the the current connection.
4779
4780 */
4781
4782 93 static void do_change_user(struct st_command *command) {
4783 93 MYSQL *mysql = &cur_con->mysql;
4784 static DYNAMIC_STRING ds_user, ds_passwd, ds_db, ds_reconnect;
4785 93 bool reconnect = true;
4786 93 const struct command_arg change_user_args[] = {
4787 {"user", ARG_STRING, false, &ds_user, "User to connect as"},
4788 {"password", ARG_STRING, false, &ds_passwd,
4789 "Password used when connecting"},
4790 {"database", ARG_STRING, false, &ds_db,
4791 "Database to select after connect"},
4792 {"reconnect", ARG_STRING, false, &ds_reconnect, "Reconnect on fail"},
4793 };
4794 93 const char *reconnect_on_fail = "reconnect_on_fail";
4795 93 const char *do_not_reconnect_on_fail = "do_not_reconnect_on_fail";
4796
4797
1/2
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
93 DBUG_TRACE;
4798
4799
1/2
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
93 check_command_args(command, command->first_argument, change_user_args,
4800 sizeof(change_user_args) / sizeof(struct command_arg),
4801 ',');
4802
4803
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
93 if (cur_con->stmt) {
4804 mysql_stmt_close(cur_con->stmt);
4805 cur_con->stmt = nullptr;
4806 }
4807
4808
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 75 times.
93 if (!ds_user.length) {
4809
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 dynstr_set(&ds_user, mysql->user);
4810
4811
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 if (!ds_passwd.length) dynstr_set(&ds_passwd, mysql->passwd);
4812
4813
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 if (!ds_db.length) dynstr_set(&ds_db, mysql->db);
4814 }
4815
4816
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 92 times.
93 if (ds_reconnect.length != 0) {
4817
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (strcmp(ds_reconnect.str, do_not_reconnect_on_fail) == 0)
4818 1 reconnect = false;
4819 else if (strcmp(ds_reconnect.str, reconnect_on_fail) == 0)
4820 reconnect = true;
4821 else
4822 die("Wrong value specified for 'reconnect' parameter. "
4823 "Allowed value are '%s' and '%s'",
4824 do_not_reconnect_on_fail, reconnect_on_fail);
4825 }
4826
4827
3/10
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 93 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 93 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
93 DBUG_PRINT("info", ("connection: '%s' user: '%s' password: '%s' "
4828 "database: '%s' reconnect: '%s'",
4829 cur_con->name, ds_user.str, ds_passwd.str, ds_db.str,
4830 reconnect ? "true" : "false"));
4831
4832
3/4
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 70 times.
93 if (mysql_change_user(mysql, ds_user.str, ds_passwd.str, ds_db.str)) {
4833
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 handle_error(curr_command, mysql_errno(mysql), mysql_error(mysql),
4834 mysql_sqlstate(mysql), &ds_res);
4835
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 1 times.
23 if (reconnect) {
4836 22 mysql->reconnect = true;
4837
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 mysql_reconnect(&cur_con->mysql);
4838 }
4839 } else
4840
1/2
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
70 handle_no_error(command);
4841
4842
1/2
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
93 dynstr_free(&ds_user);
4843
1/2
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
93 dynstr_free(&ds_passwd);
4844
1/2
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
93 dynstr_free(&ds_db);
4845
1/2
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
93 dynstr_free(&ds_reconnect);
4846 93 }
4847
4848 /*
4849 SYNOPSIS
4850 do_perl
4851 command command handle
4852
4853 DESCRIPTION
4854 perl [<delimiter>];
4855 <perlscript line 1>
4856 <...>
4857 <perlscript line n>
4858 EOF
4859
4860 Execute everything after "perl" until <delimiter> as perl.
4861 Useful for doing more advanced things
4862 but still being able to execute it on all platforms.
4863
4864 Default <delimiter> is EOF
4865 */
4866
4867 2136573 static void do_perl(struct st_command *command) {
4868 int error;
4869 File fd;
4870 FILE *res_file;
4871 char buf[FN_REFLEN + 10];
4872 char temp_file_path[FN_REFLEN];
4873 static DYNAMIC_STRING ds_script;
4874 static DYNAMIC_STRING ds_delimiter;
4875 2136573 const struct command_arg perl_args[] = {{"delimiter", ARG_STRING, false,
4876 &ds_delimiter,
4877 "Delimiter to read until"}};
4878
1/2
✓ Branch 0 taken 2136573 times.
✗ Branch 1 not taken.
2136573 DBUG_TRACE;
4879
4880
1/2
✓ Branch 0 taken 2136573 times.
✗ Branch 1 not taken.
2136573 check_command_args(command, command->first_argument, perl_args,
4881 sizeof(perl_args) / sizeof(struct command_arg), ' ');
4882
4883 2136573 ds_script = command->content;
4884 /* If it hasn't been done already by a loop iteration, fill it in */
4885
2/2
✓ Branch 0 taken 296701 times.
✓ Branch 1 taken 1839872 times.
2136573 if (!ds_script.str) {
4886 /* If no delimiter was provided, use EOF */
4887
3/4
✓ Branch 0 taken 293338 times.
✓ Branch 1 taken 3363 times.
✓ Branch 2 taken 293338 times.
✗ Branch 3 not taken.
296701 if (ds_delimiter.length == 0) dynstr_set(&ds_delimiter, "EOF");
4888
4889
1/2
✓ Branch 0 taken 296701 times.
✗ Branch 1 not taken.
296701 init_dynamic_string(&ds_script, "", 1024);
4890
1/2
✓ Branch 0 taken 296700 times.
✗ Branch 1 not taken.
296701 read_until_delimiter(&ds_script, &ds_delimiter);
4891 296700 command->content = ds_script;
4892 }
4893
4894 /* This function could be called even if "false", so check before doing */
4895
2/2
✓ Branch 0 taken 41678 times.
✓ Branch 1 taken 2094894 times.
2136572 if (cur_block->ok) {
4896
3/8
✓ Branch 0 taken 41678 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 41678 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 41678 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
41678 DBUG_PRINT("info", ("Executing perl: %s", ds_script.str));
4897
4898 /* Create temporary file name */
4899 41678 if ((fd =
4900
1/2
✓ Branch 0 taken 41678 times.
✗ Branch 1 not taken.
41678 create_temp_file(temp_file_path, getenv("MYSQLTEST_VARDIR"), "tmp",
4901
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41678 times.
41678 O_CREAT | O_RDWR, KEEP_FILE, MYF(MY_WME))) < 0)
4902 die("Failed to create temporary file for perl command");
4903
1/2
✓ Branch 0 taken 41678 times.
✗ Branch 1 not taken.
41678 my_close(fd, MYF(0));
4904
4905 /* Compatibility for Perl 5.24 and newer. */
4906
1/2
✓ Branch 0 taken 41678 times.
✗ Branch 1 not taken.
41678 std::string script = "push @INC, \".\";\n";
4907
1/2
✓ Branch 0 taken 41678 times.
✗ Branch 1 not taken.
41678 script.append(ds_script.str, ds_script.length);
4908
4909
2/4
✓ Branch 0 taken 41678 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 41678 times.
✗ Branch 3 not taken.
41678 str_to_file(temp_file_path, &script[0], script.size());
4910
4911 /* Format the "perl <filename>" command */
4912 41678 snprintf(buf, sizeof(buf), "perl %s", temp_file_path);
4913
4914
3/8
✓ Branch 0 taken 41678 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 41678 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 41678 times.
41678 if (!(res_file = popen(buf, "r")) && command->abort_on_error)
4915 die("popen(\"%s\", \"r\") failed", buf);
4916
4917
3/4
✓ Branch 0 taken 49225 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7547 times.
✓ Branch 3 taken 41678 times.
49225 while (fgets(buf, sizeof(buf), res_file)) {
4918
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7547 times.
7547 if (disable_result_log) {
4919 buf[std::strlen(buf) - 1] = 0;
4920 DBUG_PRINT("exec_result", ("%s", buf));
4921 } else {
4922
1/2
✓ Branch 0 taken 7547 times.
✗ Branch 1 not taken.
7547 replace_dynstr_append(&ds_res, buf);
4923 }
4924 }
4925
1/2
✓ Branch 0 taken 41678 times.
✗ Branch 1 not taken.
41678 error = pclose(res_file);
4926
4927 /* Remove the temporary file, but keep it if perl failed */
4928
3/4
✓ Branch 0 taken 41671 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 41671 times.
✗ Branch 3 not taken.
41678 if (!error) my_delete(temp_file_path, MYF(0));
4929
4930 /* Check for error code that indicates perl could not be started */
4931 41678 int exstat = WEXITSTATUS(error);
4932 #ifdef _WIN32
4933 if (exstat == 1) /* Text must begin 'perl not found' as mtr looks for it */
4934 abort_not_supported_test("perl not found in path or did not start");
4935 #else
4936
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41678 times.
41678 if (exstat == 127) abort_not_supported_test("perl not found in path");
4937 #endif
4938 else
4939
1/2
✓ Branch 0 taken 41672 times.
✗ Branch 1 not taken.
41678 handle_command_error(command, exstat);
4940 41672 }
4941
1/2
✓ Branch 0 taken 2136566 times.
✗ Branch 1 not taken.
2136566 dynstr_free(&ds_delimiter);
4942 2136566 }
4943
4944 /*
4945 Print the content between echo and <delimiter> to result file.
4946 Evaluate all variables in the string before printing, allow
4947 for variable names to be escaped using \
4948
4949 SYNOPSIS
4950 do_echo()
4951 command called command
4952
4953 DESCRIPTION
4954 echo text
4955 Print the text after echo until end of command to result file
4956
4957 echo $<var_name>
4958 Print the content of the variable <var_name> to result file
4959
4960 echo Some text $<var_name>
4961 Print "Some text" plus the content of the variable <var_name> to
4962 result file
4963
4964 echo Some text \$<var_name>
4965 Print "Some text" plus $<var_name> to result file
4966 */
4967
4968 1997095 static int do_echo(struct st_command *command) {
4969 DYNAMIC_STRING ds_echo;
4970
1/2
✓ Branch 0 taken 1997095 times.
✗ Branch 1 not taken.
1997095 DBUG_TRACE;
4971
4972
1/2
✓ Branch 0 taken 1997095 times.
✗ Branch 1 not taken.
1997095 init_dynamic_string(&ds_echo, "", command->query_len);
4973
1/2
✓ Branch 0 taken 1997095 times.
✗ Branch 1 not taken.
1997095 do_eval(&ds_echo, command->first_argument, command->end, false);
4974
1/2
✓ Branch 0 taken 1997095 times.
✗ Branch 1 not taken.
1997095 dynstr_append_mem(&ds_res, ds_echo.str, ds_echo.length);
4975
1/2
✓ Branch 0 taken 1997095 times.
✗ Branch 1 not taken.
1997095 dynstr_append_mem(&ds_res, "\n", 1);
4976
1/2
✓ Branch 0 taken 1997095 times.
✗ Branch 1 not taken.
1997095 dynstr_free(&ds_echo);
4977 1997095 command->last_argument = command->end;
4978 1997095 return 0;
4979 1997095 }
4980
4981 208 static void do_wait_for_slave_to_stop(struct st_command *c [[maybe_unused]]) {
4982 static int SLAVE_POLL_INTERVAL = 300000;
4983 208 MYSQL *mysql = &cur_con->mysql;
4984 for (;;) {
4985 261 MYSQL_RES *res = nullptr;
4986 MYSQL_ROW row;
4987 int done;
4988
4989 261 if (mysql_query_wrapper(
4990 mysql,
4991 "SELECT 'Slave_running' as Variable_name,"
4992 " IF(count(*)>0,'ON','OFF') as Value FROM"
4993 " performance_schema.replication_applier_status ras,"
4994 "performance_schema.replication_connection_status rcs WHERE "
4995
2/4
✓ Branch 0 taken 261 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 261 times.
522 "ras.SERVICE_STATE='ON' AND rcs.SERVICE_STATE='ON'") ||
4996
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 261 times.
261 !(res = mysql_store_result_wrapper(mysql)))
4997
4998 die("Query failed while probing slave for stop: %s", mysql_error(mysql));
4999
5000
3/6
✓ Branch 0 taken 261 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 261 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 261 times.
261 if (!(row = mysql_fetch_row_wrapper(res)) || !row[1]) {
5001 mysql_free_result_wrapper(res);
5002 die("Strange result from query while probing slave for stop");
5003 }
5004 261 done = !std::strcmp(row[1], "OFF");
5005 261 mysql_free_result_wrapper(res);
5006
2/2
✓ Branch 0 taken 208 times.
✓ Branch 1 taken 53 times.
261 if (done) break;
5007 53 my_sleep(SLAVE_POLL_INTERVAL);
5008 53 }
5009 208 return;
5010 }
5011
5012 1999 static void do_sync_with_master2(struct st_command *command, long offset) {
5013 MYSQL_RES *res;
5014 MYSQL_ROW row;
5015 1999 MYSQL *mysql = &cur_con->mysql;
5016 char query_buf[FN_REFLEN + 128];
5017 1999 int timeout = 300; /* seconds */
5018
5019
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1999 times.
1999 if (!master_pos.file[0])
5020 die("Calling 'sync_with_master' without calling 'save_master_pos'");
5021
5022 1999 sprintf(query_buf, "select source_pos_wait('%s', %ld, %d)", master_pos.file,
5023 1999 master_pos.pos + offset, timeout);
5024
5025
2/4
✓ Branch 0 taken 1999 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1999 times.
1999 if (mysql_query_wrapper(mysql, query_buf))
5026 die("failed in '%s': %d: %s", query_buf, mysql_errno(mysql),
5027 mysql_error(mysql));
5028
5029
2/4
✓ Branch 0 taken 1999 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1999 times.
1999 if (!(res = mysql_store_result_wrapper(mysql)))
5030 die("mysql_store_result() returned NULL for '%s'", query_buf);
5031
2/4
✓ Branch 0 taken 1999 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1999 times.
1999 if (!(row = mysql_fetch_row_wrapper(res))) {
5032 mysql_free_result_wrapper(res);
5033 die("empty result in %s", query_buf);
5034 }
5035
5036 1999 int result = -99;
5037 1999 const char *result_str = row[0];
5038
1/2
✓ Branch 0 taken 1999 times.
✗ Branch 1 not taken.
1999 if (result_str) result = atoi(result_str);
5039
5040
1/2
✓ Branch 0 taken 1999 times.
✗ Branch 1 not taken.
1999 mysql_free_result_wrapper(res);
5041
5042
3/4
✓ Branch 0 taken 1999 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1997 times.
1999 if (!result_str || result < 0) {
5043 /* source_pos_wait returned NULL or < 0 */
5044
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 show_query(mysql, "SHOW MASTER STATUS");
5045
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 show_query(mysql, "SHOW SLAVE STATUS");
5046
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 show_query(mysql, "SHOW PROCESSLIST");
5047
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 fprintf(stderr, "analyze: sync_with_master\n");
5048
5049
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!result_str) {
5050 /*
5051 source_pos_wait returned NULL. This indicates that
5052 slave SQL thread is not started, the slave's master
5053 information is not initialized, the arguments are
5054 incorrect, or an error has occurred
5055 */
5056 die("%.*s failed: '%s' returned NULL "
5057 "indicating slave SQL thread failure",
5058 static_cast<int>(command->first_word_len), command->query, query_buf);
5059 }
5060
5061
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (result == -1)
5062 2 die("%.*s failed: '%s' returned -1 "
5063 "indicating timeout after %d seconds",
5064 2 static_cast<int>(command->first_word_len), command->query, query_buf,
5065 timeout);
5066 else
5067 die("%.*s failed: '%s' returned unknown result :%d",
5068 static_cast<int>(command->first_word_len), command->query, query_buf,
5069 result);
5070 }
5071
5072 3994 return;
5073 }
5074
5075 185 static void do_sync_with_master(struct st_command *command) {
5076 185 long offset = 0;
5077 185 char *p = command->first_argument;
5078 185 const char *offset_start = p;
5079
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 179 times.
185 if (*offset_start) {
5080
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 for (; my_isdigit(charset_info, *p); p++) offset = offset * 10 + *p - '0';
5081
5082
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
6 if (*p && !my_isspace(charset_info, *p))
5083 2 die("Invalid integer argument \"%s\"", offset_start);
5084 4 command->last_argument = p;
5085 }
5086 183 do_sync_with_master2(command, offset);
5087 183 return;
5088 }
5089
5090 /*
5091 Wait for ndb binlog injector to be up-to-date with all changes
5092 done on the local mysql server
5093 */
5094
5095 2067 static void ndb_wait_for_binlog_injector(void) {
5096 MYSQL_RES *res;
5097 MYSQL_ROW row;
5098 2067 MYSQL *mysql = &cur_con->mysql;
5099 const char *query;
5100 ulong have_ndbcluster;
5101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2067 times.
2067 if (mysql_query_wrapper(
5102 mysql, query = "select count(*) from information_schema.engines"
5103 " where engine = 'ndbcluster' and"
5104 " support in ('YES', 'DEFAULT')"))
5105 die("'%s' failed: %d %s", query, mysql_errno(mysql), mysql_error(mysql));
5106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2067 times.
2067 if (!(res = mysql_store_result_wrapper(mysql)))
5107 die("mysql_store_result() returned NULL for '%s'", query);
5108
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2067 times.
2067 if (!(row = mysql_fetch_row_wrapper(res)))
5109 die("Query '%s' returned empty result", query);
5110
5111 2067 have_ndbcluster = std::strcmp(row[0], "1") == 0;
5112 2067 mysql_free_result_wrapper(res);
5113
5114
1/2
✓ Branch 0 taken 2067 times.
✗ Branch 1 not taken.
2067 if (!have_ndbcluster) {
5115 2067 return;
5116 }
5117
5118 ulonglong start_epoch = 0, handled_epoch = 0, latest_trans_epoch = 0,
5119 latest_handled_binlog_epoch = 0, start_handled_binlog_epoch = 0;
5120 const int WaitSeconds = 150;
5121
5122 int count = 0;
5123 int do_continue = 1;
5124 while (do_continue) {
5125 const char binlog[] = "binlog";
5126 const char latest_trans_epoch_str[] = "latest_trans_epoch=";
5127 const char latest_handled_binlog_epoch_str[] =
5128 "latest_handled_binlog_epoch=";
5129 if (count) my_sleep(100 * 1000); /* 100ms */
5130
5131 if (mysql_query_wrapper(mysql, query = "show engine ndb status"))
5132 die("failed in '%s': %d %s", query, mysql_errno(mysql),
5133 mysql_error(mysql));
5134
5135 if (!(res = mysql_store_result_wrapper(mysql)))
5136 die("mysql_store_result() returned NULL for '%s'", query);
5137
5138 while ((row = mysql_fetch_row_wrapper(res))) {
5139 if (std::strcmp(row[1], binlog) == 0) {
5140 const char *status = row[2];
5141
5142 /* latest_trans_epoch */
5143 while (*status && std::strncmp(status, latest_trans_epoch_str,
5144 sizeof(latest_trans_epoch_str) - 1))
5145 status++;
5146 if (*status) {
5147 status += sizeof(latest_trans_epoch_str) - 1;
5148 latest_trans_epoch = my_strtoull(status, (char **)nullptr, 10);
5149 } else
5150 die("result does not contain '%s' in '%s'", latest_trans_epoch_str,
5151 query);
5152
5153 /* latest_handled_binlog */
5154 while (*status &&
5155 std::strncmp(status, latest_handled_binlog_epoch_str,
5156 sizeof(latest_handled_binlog_epoch_str) - 1))
5157 status++;
5158 if (*status) {
5159 status += sizeof(latest_handled_binlog_epoch_str) - 1;
5160 latest_handled_binlog_epoch =
5161 my_strtoull(status, (char **)nullptr, 10);
5162 } else
5163 die("result does not contain '%s' in '%s'",
5164 latest_handled_binlog_epoch_str, query);
5165
5166 if (count == 0) {
5167 start_epoch = latest_trans_epoch;
5168 start_handled_binlog_epoch = latest_handled_binlog_epoch;
5169 }
5170 break;
5171 }
5172 }
5173 if (!row) die("result does not contain '%s' in '%s'", binlog, query);
5174 if (latest_handled_binlog_epoch > handled_epoch) count = 0;
5175 handled_epoch = latest_handled_binlog_epoch;
5176 count++;
5177 if (latest_handled_binlog_epoch >= start_epoch)
5178 do_continue = 0;
5179 else if (count > (WaitSeconds * 10)) {
5180 die("do_save_master_pos() timed out after %u s waiting for "
5181 "last committed epoch to be applied by the "
5182 "Ndb binlog injector. "
5183 "Ndb epoch %llu/%llu to be handled. "
5184 "Last handled epoch : %llu/%llu. "
5185 "First handled epoch : %llu/%llu.",
5186 WaitSeconds, start_epoch >> 32, start_epoch & 0xffffffff,
5187 latest_handled_binlog_epoch >> 32,
5188 latest_handled_binlog_epoch & 0xffffffff,
5189 start_handled_binlog_epoch >> 32,
5190 start_handled_binlog_epoch & 0xffffffff);
5191 }
5192
5193 mysql_free_result_wrapper(res);
5194 }
5195 }
5196
5197 2067 static int do_save_master_pos() {
5198 MYSQL_RES *res;
5199 MYSQL_ROW row;
5200 2067 MYSQL *mysql = &cur_con->mysql;
5201 const char *query;
5202
1/2
✓ Branch 0 taken 2067 times.
✗ Branch 1 not taken.
2067 DBUG_TRACE;
5203 /*
5204 when ndb binlog is on, this call will wait until last updated epoch
5205 (locally in the mysqld) has been received into the binlog
5206 */
5207
1/2
✓ Branch 0 taken 2067 times.
✗ Branch 1 not taken.
2067 ndb_wait_for_binlog_injector();
5208
5209
2/4
✓ Branch 0 taken 2067 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2067 times.
2067 if (mysql_query_wrapper(mysql, query = "show master status"))
5210 die("failed in 'show master status': %d %s", mysql_errno(mysql),
5211 mysql_error(mysql));
5212
5213
2/4
✓ Branch 0 taken 2067 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2067 times.
2067 if (!(res = mysql_store_result_wrapper(mysql)))
5214 die("mysql_store_result() retuned NULL for '%s'", query);
5215
2/4
✓ Branch 0 taken 2067 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2067 times.
2067 if (!(row = mysql_fetch_row_wrapper(res)))
5216 die("empty result in show master status");
5217 2067 my_stpnmov(master_pos.file, row[0], sizeof(master_pos.file) - 1);
5218 2067 master_pos.pos = strtoul(row[1], (char **)nullptr, 10);
5219
1/2
✓ Branch 0 taken 2067 times.
✗ Branch 1 not taken.
2067 mysql_free_result_wrapper(res);
5220 2067 return 0;
5221 2067 }
5222
5223 /*
5224 Check if a variable name is valid or not.
5225
5226 SYNOPSIS
5227 check_variable_name()
5228 var_name - pointer to the beginning of variable name
5229 var_name_end - pointer to the end of variable name
5230 dollar_flag - flag to check whether variable name should start with '$'
5231 */
5232 203556 static void check_variable_name(const char *var_name, const char *var_name_end,
5233 const bool dollar_flag) {
5234 char save_var_name[MAX_VAR_NAME_LENGTH];
5235
1/2
✓ Branch 0 taken 203556 times.
✗ Branch 1 not taken.
203556 strmake(save_var_name, var_name, (var_name_end - var_name));
5236
5237 // Check if variable name should start with '$'
5238
4/4
✓ Branch 0 taken 135702 times.
✓ Branch 1 taken 67854 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 135701 times.
203556 if (!dollar_flag && (*var_name != '$'))
5239 1 die("Variable name '%s' should start with '$'", save_var_name);
5240
5241
2/2
✓ Branch 0 taken 203554 times.
✓ Branch 1 taken 1 times.
203555 if (*var_name == '$') var_name++;
5242
5243 // Check if variable name exists or not
5244
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 203554 times.
203555 if (var_name == var_name_end) die("Missing variable name.");
5245
5246 // Check for non alphanumeric character(s) in variable name
5247
6/6
✓ Branch 0 taken 2054169 times.
✓ Branch 1 taken 203552 times.
✓ Branch 2 taken 1910754 times.
✓ Branch 3 taken 143415 times.
✓ Branch 4 taken 143413 times.
✓ Branch 5 taken 2 times.
2257721 while ((var_name != var_name_end) && my_isvar(charset_info, *var_name))
5248 2054167 var_name++;
5249
5250
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 203552 times.
203554 if (var_name != var_name_end)
5251 2 die("Invalid variable name '%s'", save_var_name);
5252 203552 }
5253
5254 /*
5255 Check if the pointer points to an operator.
5256
5257 SYNOPSIS
5258 is_operator()
5259 op - character pointer to mathematical expression
5260 */
5261 1135877 static bool is_operator(const char *op) {
5262
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1135877 times.
1135877 if (*op == '+')
5263 return true;
5264
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1135877 times.
1135877 else if (*op == '-')
5265 return true;
5266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1135877 times.
1135877 else if (*op == '*')
5267 return true;
5268
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1135877 times.
1135877 else if (*op == '/')
5269 return true;
5270
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1135877 times.
1135877 else if (*op == '%')
5271 return true;
5272
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1135877 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1135877 else if (*op == '&' && *(op + 1) == '&')
5273 return true;
5274
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1135877 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1135877 else if (*op == '|' && *(op + 1) == '|')
5275 return true;
5276
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1135877 times.
1135877 else if (*op == '&')
5277 return true;
5278
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1135877 times.
1135877 else if (*op == '|')
5279 return true;
5280
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1135877 times.
1135877 else if (*op == '^')
5281 return true;
5282
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1135877 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1135877 else if (*op == '>' && *(op + 1) == '>')
5283 return true;
5284
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1135877 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1135877 else if (*op == '<' && *(op + 1) == '<')
5285 return true;
5286
5287 1135877 return false;
5288 }
5289
5290 /*
5291 Perform basic mathematical operation.
5292
5293 SYNOPSIS
5294 do_expr()
5295 command - command handle
5296
5297 DESCRIPTION
5298 expr $<var_name>= <operand1> <operator> <operand2>
5299 Perform basic mathematical operation and store the result
5300 in a variable($<var_name>). Both <operand1> and <operand2>
5301 should be valid MTR variables.
5302
5303 'expr' command supports only binary operators that operates
5304 on two operands and manipulates them to return a result.
5305
5306 Following mathematical operators are supported.
5307 1 Arithmetic Operators
5308 1.1 Addition
5309 1.2 Subtraction
5310 1.3 Multiplication
5311 1.4 Division
5312 1.5 Modulo
5313
5314 2 Logical Operators
5315 2.1 Logical AND
5316 2.2 Logical OR
5317
5318 3 Bitwise Operators
5319 3.1 Binary AND
5320 3.2 Binary OR
5321 3.3 Binary XOR
5322 3.4 Binary Left Shift
5323 3.5 Binary Right Shift
5324
5325 NOTE
5326 1. Non-integer operand is truncated to integer value for operations
5327 that dont support non-integer operand.
5328 2. If the result is an infinite value, then expr command will return
5329 'inf' keyword to indicate the result is infinity.
5330 3. Division by 0 will result in an infinite value and expr command
5331 will return 'inf' keyword to indicate the result is infinity.
5332 */
5333 67855 static void do_expr(struct st_command *command) {
5334
1/2
✓ Branch 0 taken 67855 times.
✗ Branch 1 not taken.
67855 DBUG_TRACE;
5335
5336 67855 const char *p = command->first_argument;
5337
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 67854 times.
67855 if (!*p) die("Missing arguments to expr command.");
5338
5339 // Find <var_name>
5340 67854 const char *var_name = p;
5341
6/6
✓ Branch 0 taken 563838 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 563614 times.
✓ Branch 3 taken 224 times.
✓ Branch 4 taken 495985 times.
✓ Branch 5 taken 67629 times.
563839 while (*p && (*p != '=') && !my_isspace(charset_info, *p)) p++;
5342 67854 const char *var_name_end = p;
5343
1/2
✓ Branch 0 taken 67853 times.
✗ Branch 1 not taken.
67854 check_variable_name(var_name, var_name_end, true);
5344
5345 // Skip spaces between <var_name> and '='
5346
2/2
✓ Branch 0 taken 67629 times.
✓ Branch 1 taken 67853 times.
135482 while (my_isspace(charset_info, *p)) p++;
5347
5348
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 67852 times.
67853 if (*p++ != '=') die("Missing assignment operator in expr command.");
5349
5350 // Skip spaces after '='
5351
3/4
✓ Branch 0 taken 135703 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67851 times.
✓ Branch 3 taken 67852 times.
135703 while (*p && my_isspace(charset_info, *p)) p++;
5352
5353 // Save the mathematical expression in a variable
5354 67852 const char *expr = p;
5355
5356 // First operand in the expression
5357 67852 const char *operand_name = p;
5358
7/8
✓ Branch 0 taken 1135877 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1135877 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1068026 times.
✓ Branch 5 taken 67851 times.
✓ Branch 6 taken 1068026 times.
✓ Branch 7 taken 67852 times.
1135878 while (*p && !is_operator(p) && !my_isspace(charset_info, *p)) p++;
5359 67852 const char *operand_name_end = p;
5360
1/2
✓ Branch 0 taken 67852 times.
✗ Branch 1 not taken.
67852 check_variable_name(operand_name, operand_name_end, false);
5361
1/2
✓ Branch 0 taken 67852 times.
✗ Branch 1 not taken.
67852 VAR *v1 = var_get(operand_name, &operand_name_end, false, false);
5362
5363 double operand1;
5364
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 67842 times.
67852 if ((my_isdigit(charset_info, *v1->str_val)) ||
5365
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
10 ((*v1->str_val == '-') && my_isdigit(charset_info, *(v1->str_val + 1))))
5366 67850 operand1 = strtod(v1->str_val, nullptr);
5367 else
5368 2 die("Undefined/invalid first operand '$%s' in expr command.", v1->name);
5369
5370 // Skip spaces after the first operand
5371
3/4
✓ Branch 0 taken 135700 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67850 times.
✓ Branch 3 taken 67850 times.
135700 while (*p && my_isspace(charset_info, *p)) p++;
5372
5373 // Extract the operator
5374 67850 const char *operator_start = p;
5375
2/4
✓ Branch 0 taken 135718 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 135718 times.
✗ Branch 3 not taken.
135718 while (*p && (*p != '$') &&
5376
4/6
✓ Branch 0 taken 67868 times.
✓ Branch 1 taken 67850 times.
✓ Branch 2 taken 67868 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 67868 times.
✗ Branch 5 not taken.
135718 !(my_isspace(charset_info, *p) || my_isvar(charset_info, *p)))
5377 67868 p++;
5378
5379 char math_operator[3];
5380
1/2
✓ Branch 0 taken 67850 times.
✗ Branch 1 not taken.
67850 strmake(math_operator, operator_start, (p - operator_start));
5381
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 67850 times.
67850 if (!std::strlen(math_operator))
5382 die("Missing mathematical operator in expr command.");
5383
5384 // Skip spaces after the operator
5385
3/4
✓ Branch 0 taken 135700 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67850 times.
✓ Branch 3 taken 67850 times.
135700 while (*p && my_isspace(charset_info, *p)) p++;
5386
5387 // Second operand in the expression
5388 67850 operand_name = p;
5389
4/4
✓ Branch 0 taken 693716 times.
✓ Branch 1 taken 67849 times.
✓ Branch 2 taken 693715 times.
✓ Branch 3 taken 1 times.
761565 while (*p && !my_isspace(charset_info, *p)) p++;
5390 67850 operand_name_end = p;
5391
1/2
✓ Branch 0 taken 67847 times.
✗ Branch 1 not taken.
67850 check_variable_name(operand_name, operand_name_end, false);
5392
1/2
✓ Branch 0 taken 67847 times.
✗ Branch 1 not taken.
67847 VAR *v2 = var_get(operand_name, &operand_name_end, false, false);
5393
5394 double operand2;
5395
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 67830 times.
67847 if ((my_isdigit(charset_info, *v2->str_val)) ||
5396
4/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 1 times.
17 ((*v2->str_val == '-') && my_isdigit(charset_info, *(v2->str_val + 1))))
5397 67845 operand2 = strtod(v2->str_val, nullptr);
5398 else
5399 2 die("Undefined/invalid second operand '$%s' in expr command.", v2->name);
5400
5401 // Skip spaces at the end
5402
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 67844 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
67846 while (*p && my_isspace(charset_info, *p)) p++;
5403
5404 // Check for any spurious text after the second operand
5405
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 67844 times.
67845 if (*p) die("Invalid mathematical expression '%s' in expr command.", expr);
5406
5407 double result;
5408 // Arithmetic Operators
5409
2/2
✓ Branch 0 taken 1786 times.
✓ Branch 1 taken 66058 times.
67844 if (!std::strcmp(math_operator, "+"))
5410 1786 result = operand1 + operand2;
5411
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 66047 times.
66058 else if (!std::strcmp(math_operator, "-"))
5412 11 result = operand1 - operand2;
5413
2/2
✓ Branch 0 taken 66009 times.
✓ Branch 1 taken 38 times.
66047 else if (!std::strcmp(math_operator, "*"))
5414 66009 result = operand1 * operand2;
5415
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 32 times.
38 else if (!std::strcmp(math_operator, "/")) {
5416
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 if (operand2 == 0.0) {
5417
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (operand1 == 0.0)
5418 1 result = std::numeric_limits<double>::quiet_NaN();
5419 else
5420 1 result = std::numeric_limits<double>::infinity();
5421 } else
5422 4 result = operand1 / operand2;
5423
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 29 times.
32 } else if (!std::strcmp(math_operator, "%"))
5424 3 result = (int)operand1 % (int)operand2;
5425 // Logical Operators
5426
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 23 times.
29 else if (!std::strcmp(math_operator, "&&"))
5427
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 2 times.
6 result = operand1 && operand2;
5428
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 else if (!std::strcmp(math_operator, "||"))
5429
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
5 result = operand1 || operand2;
5430 // Bitwise Operators
5431
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
18 else if (!std::strcmp(math_operator, "&"))
5432 4 result = (int)operand1 & (int)operand2;
5433
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10 times.
14 else if (!std::strcmp(math_operator, "|"))
5434 4 result = (int)operand1 | (int)operand2;
5435
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 7 times.
10 else if (!std::strcmp(math_operator, "^"))
5436 3 result = (int)operand1 ^ (int)operand2;
5437
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 else if (!std::strcmp(math_operator, ">>"))
5438 3 result = (int)operand1 >> (int)operand2;
5439
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 else if (!std::strcmp(math_operator, "<<"))
5440 3 result = (int)operand1 << (int)operand2;
5441 else
5442 1 die("Invalid operator '%s' in expr command", math_operator);
5443
5444 char buf[128];
5445 size_t result_len;
5446 // Check if result is an infinite value
5447
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 67842 times.
67843 if (std::isnan(result)) {
5448 // Print 'nan' if result is Not a Number
5449 1 result_len = snprintf(buf, sizeof(buf), "%s", "nan");
5450
2/2
✓ Branch 0 taken 67840 times.
✓ Branch 1 taken 2 times.
67842 } else if (!std::isinf(result)) {
5451
3/4
✓ Branch 0 taken 67837 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 67837 times.
✗ Branch 3 not taken.
67840 const char *format = (result < 1e10 && result > -1e10) ? "%f" : "%g";
5452 67840 result_len = snprintf(buf, sizeof(buf), format, result);
5453 } else
5454 // Print 'inf' if result is an infinite value
5455 2 result_len = snprintf(buf, sizeof(buf), "%s", "inf");
5456
5457
3/4
✓ Branch 0 taken 67837 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 67837 times.
✗ Branch 3 not taken.
67843 if (result < 1e10 && result > -1e10) {
5458 /*
5459 Remove the trailing 0's i.e 2.0000000 need to be represented
5460 as 2 for consistency, 2.0010000 also becomes 2.001.
5461 */
5462
2/2
✓ Branch 0 taken 407017 times.
✓ Branch 1 taken 67837 times.
474854 while (buf[result_len - 1] == '0') result_len--;
5463
5464 // Remove trailing '.' if exists
5465
2/2
✓ Branch 0 taken 67833 times.
✓ Branch 1 taken 4 times.
67837 if (buf[result_len - 1] == '.') result_len--;
5466 }
5467
5468
1/2
✓ Branch 0 taken 67843 times.
✗ Branch 1 not taken.
67843 var_set(var_name, var_name_end, buf, buf + result_len);
5469 67843 command->last_argument = command->end;
5470 67843 }
5471
5472 /*
5473 Assign the variable <var_name> with <var_val>
5474
5475 SYNOPSIS
5476 do_let()
5477 query called command
5478
5479 DESCRIPTION
5480 let $<var_name>=<var_val><delimiter>
5481
5482 <var_name> - is the string string found between the $ and =
5483 <var_val> - is the content between the = and <delimiter>, it may span
5484 multiple line and contain any characters except <delimiter>
5485 <delimiter> - is a string containing of one or more chars, default is ;
5486
5487 RETURN VALUES
5488 Program will die if error detected
5489 */
5490
5491 25335431 static void do_let(struct st_command *command) {
5492 25335431 const char *p = command->first_argument;
5493 const char *var_name, *var_name_end;
5494 DYNAMIC_STRING let_rhs_expr;
5495
1/2
✓ Branch 0 taken 25335431 times.
✗ Branch 1 not taken.
25335431 DBUG_TRACE;
5496
5497
1/2
✓ Branch 0 taken 25335431 times.
✗ Branch 1 not taken.
25335431 init_dynamic_string(&let_rhs_expr, "", 512);
5498
5499 /* Find <var_name> */
5500
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 25335430 times.
25335431 if (!*p) die("Missing arguments to let");
5501 25335430 var_name = p;
5502
6/6
✓ Branch 0 taken 518994455 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 501446079 times.
✓ Branch 3 taken 17548376 times.
✓ Branch 4 taken 493659027 times.
✓ Branch 5 taken 7787052 times.
518994457 while (*p && (*p != '=') && !my_isspace(charset_info, *p)) p++;
5503 25335430 var_name_end = p;
5504
2/2
✓ Branch 0 taken 25335429 times.
✓ Branch 1 taken 1 times.
25335430 if (var_name == var_name_end ||
5505
4/4
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 25335400 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 27 times.
25335429 (var_name + 1 == var_name_end && *var_name == '$'))
5506 3 die("Missing variable name in let");
5507
2/2
✓ Branch 0 taken 24140377 times.
✓ Branch 1 taken 25335427 times.
49475804 while (my_isspace(charset_info, *p)) p++;
5508
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 25335423 times.
25335427 if (*p++ != '=') die("Missing assignment operator in let");
5509
5510 /* Find start of <var_val> */
5511
4/4
✓ Branch 0 taken 45712793 times.
✓ Branch 1 taken 2410529 times.
✓ Branch 2 taken 22787899 times.
✓ Branch 3 taken 22924894 times.
48123322 while (*p && my_isspace(charset_info, *p)) p++;
5512
5513
1/2
✓ Branch 0 taken 25335423 times.
✗ Branch 1 not taken.
25335423 do_eval(&let_rhs_expr, p, command->end, false);
5514
5515 25335423 command->last_argument = command->end;
5516 /* Assign var_val to var_name */
5517 25335423 var_set(var_name, var_name_end, let_rhs_expr.str,
5518
1/2
✓ Branch 0 taken 25335396 times.
✗ Branch 1 not taken.
25335423 (let_rhs_expr.str + let_rhs_expr.length));
5519
1/2
✓ Branch 0 taken 25335396 times.
✗ Branch 1 not taken.
25335396 dynstr_free(&let_rhs_expr);
5520
1/2
✓ Branch 0 taken 25335396 times.
✗ Branch 1 not taken.
25335396 revert_properties();
5521 25335396 }
5522
5523 /*
5524 Sleep the number of specified seconds
5525
5526 SYNOPSIS
5527 do_sleep()
5528 q called command
5529
5530 DESCRIPTION
5531 Sleep <seconds>
5532
5533 The argument provided to --sleep command is not required to be
5534 a whole number and can have fractional parts as well. For
5535 example, '--sleep 0.1' is valid.
5536
5537 */
5538
5539 1780166 static int do_sleep(struct st_command *command) {
5540 1780166 int error = 0;
5541 double sleep_val;
5542 char *p;
5543 static DYNAMIC_STRING ds_sleep;
5544 1780166 const struct command_arg sleep_args[] = {{"sleep_delay", ARG_STRING, true,
5545 &ds_sleep,
5546 "Number of seconds to sleep."}};
5547
1/2
✓ Branch 0 taken 1780162 times.
✗ Branch 1 not taken.
1780166 check_command_args(command, command->first_argument, sleep_args,
5548 sizeof(sleep_args) / sizeof(struct command_arg), ' ');
5549
5550 1780162 p = ds_sleep.str;
5551 1780162 const char *sleep_end = ds_sleep.str + ds_sleep.length;
5552
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1780162 times.
1780162 while (my_isspace(charset_info, *p)) p++;
5553
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1780162 times.
1780162 if (!*p)
5554 die("Missing argument to %.*s", static_cast<int>(command->first_word_len),
5555 command->query);
5556 1780162 const char *sleep_start = p;
5557 /* Check that arg starts with a digit, not handled by my_strtod */
5558
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1780160 times.
1780162 if (!my_isdigit(charset_info, *sleep_start))
5559 2 die("Invalid argument to %.*s \"%s\"",
5560 2 static_cast<int>(command->first_word_len), command->query, sleep_start);
5561
1/2
✓ Branch 0 taken 1780160 times.
✗ Branch 1 not taken.
1780160 sleep_val = my_strtod(sleep_start, &sleep_end, &error);
5562
1/2
✓ Branch 0 taken 1780159 times.
✗ Branch 1 not taken.
1780160 check_eol_junk_line(sleep_end);
5563
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1780159 times.
1780159 if (error)
5564 die("Invalid argument to %.*s \"%s\"",
5565 static_cast<int>(command->first_word_len), command->query,
5566 command->first_argument);
5567
1/2
✓ Branch 0 taken 1780159 times.
✗ Branch 1 not taken.
1780159 dynstr_free(&ds_sleep);
5568
5569
3/8
✓ Branch 0 taken 1780159 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1780159 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1780159 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1780159 DBUG_PRINT("info", ("sleep_val: %f", sleep_val));
5570
2/4
✓ Branch 0 taken 1780159 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1780159 times.
✗ Branch 3 not taken.
1780159 if (sleep_val) my_sleep((ulong)(sleep_val * 1000000L));
5571 1780159 return 0;
5572 }
5573
5574 271 static void do_set_charset(struct st_command *command) {
5575 271 char *charset_name = command->first_argument;
5576 char *p;
5577
5578
2/4
✓ Branch 0 taken 271 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 271 times.
271 if (!charset_name || !*charset_name)
5579 die("Missing charset name in 'character_set'");
5580 /* Remove end space */
5581 271 p = charset_name;
5582
3/4
✓ Branch 0 taken 1441 times.
✓ Branch 1 taken 271 times.
✓ Branch 2 taken 1441 times.
✗ Branch 3 not taken.
1712 while (*p && !my_isspace(charset_info, *p)) p++;
5583
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 271 times.
271 if (*p) *p++ = 0;
5584 271 command->last_argument = p;
5585 271 charset_info =
5586 271 get_charset_by_csname(charset_name, MY_CS_PRIMARY, MYF(MY_WME));
5587
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 271 times.
271 if (!charset_info)
5588 abort_not_supported_test("Test requires charset '%s'", charset_name);
5589 271 }
5590
5591 /// Check if the bug number argument to disable_testcase is in a proper
5592 /// format.
5593 ///
5594 /// Bug number argument should follow 'BUG#XXXX' format
5595 ///
5596 /// - Keyword 'BUG" is case-insensitive
5597 /// - XXXX should contain only digits
5598 ///
5599 /// @param bug_number String representing a bug number
5600 ///
5601 /// @retval True if the bug number argument is in correct format, false
5602 /// otherwise.
5603 106 static bool validate_bug_number_argument(std::string bug_number) {
5604 // Convert the string to lowercase characters
5605 106 std::transform(bug_number.begin(), bug_number.end(), bug_number.begin(),
5606 ::tolower);
5607
5608 // Check if string representing a bug number starts 'BUG' keyword.
5609 // Note: This keyword is case-inseinsitive.
5610
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 if (bug_number.substr(0, 3).compare("bug") != 0) return false;
5611
5612 // Check if the string contains '#' after 'BUG' keyword
5613
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 105 times.
106 if (bug_number.at(3) != '#') return false;
5614
5615 // Check if the bug number string contains only digits after '#'
5616
3/4
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 103 times.
105 if (get_int_val(bug_number.substr(4).c_str()) == -1) return false;
5617
5618 103 return true;
5619 }
5620
5621 /// Disable or don't execute the statements or commands appear after
5622 /// this command until the execution is enabled by 'enable_testcase'
5623 /// command. If test cases are already disabled, then throw an error
5624 /// and abort the test execution.
5625 ///
5626 /// This command also takes a mandatory parameter specifying the bug
5627 /// number.
5628 ///
5629 /// @code
5630 /// --disable_testcase BUG#XXXX
5631 /// statements or commands
5632 /// --enable_testcase
5633 /// @endcode
5634 ///
5635 /// @param command Pointer to the st_command structure which holds the
5636 /// arguments and information for the command.
5637 106 static void do_disable_testcase(struct st_command *command) {
5638 DYNAMIC_STRING ds_bug_number;
5639 106 const struct command_arg disable_testcase_args[] = {
5640 106 {"Bug number", ARG_STRING, true, &ds_bug_number, "Bug number"}};
5641
5642
1/2
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
106 check_command_args(command, command->first_argument, disable_testcase_args,
5643 sizeof(disable_testcase_args) / sizeof(struct command_arg),
5644 ' ');
5645
5646 /// Check if the bug number argument to disable_testcase is in a
5647 /// proper format.
5648
4/6
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 106 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 103 times.
106 if (validate_bug_number_argument(ds_bug_number.str) == 0) {
5649
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 free_dynamic_strings(&ds_bug_number);
5650 3 die("Bug number mentioned in '%s' command is not in a correct format. "
5651 "It should be 'BUG#XXXX', where keyword 'BUG' is case-insensitive "
5652 "and 'XXXX' should contain only digits.",
5653 command->query);
5654 }
5655
5656 103 testcase_disabled = true;
5657
1/2
✓ Branch 0 taken 103 times.
✗ Branch 1 not taken.
103 free_dynamic_strings(&ds_bug_number);
5658 103 }
5659
5660 /**
5661 A wrapper around kill call that prints diagnostics if the call failed with
5662 any other error than ESRCH.
5663
5664 @param pid Process id
5665 @param sig Signal to send to process
5666 @return The return value of kill call
5667 */
5668 41604 static int my_kill(int pid, int sig) {
5669 41604 const int result = kill(pid, sig);
5670
3/4
✓ Branch 0 taken 4674 times.
✓ Branch 1 taken 36930 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4674 times.
41604 if (result == -1 && errno != ESRCH) {
5671 log_msg("kill(%d, %d) returned errno %d (%s)", pid, sig, errno,
5672 strerror(errno));
5673 }
5674 41604 return result;
5675 }
5676
5677 /**
5678 Check if process is active.
5679
5680 @param pid Process id.
5681
5682 @return true if process is active, false otherwise.
5683 */
5684 41064 static bool is_process_active(int pid) {
5685 #ifdef _WIN32
5686 DWORD exit_code;
5687 HANDLE proc;
5688 proc = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid);
5689 if (proc == NULL) return false; /* Process could not be found. */
5690
5691 if (!GetExitCodeProcess(proc, &exit_code)) exit_code = 0;
5692
5693 CloseHandle(proc);
5694 if (exit_code != STILL_ACTIVE)
5695 return false; /* Error or process has terminated. */
5696
5697 return true;
5698 #else
5699 41064 return (my_kill(pid, 0) == 0);
5700 #endif
5701 }
5702
5703 /**
5704 kill process.
5705
5706 @param pid Process id.
5707
5708 @return true if process is terminated, false otherwise.
5709 */
5710 516 static bool kill_process(int pid) {
5711 516 bool killed = true;
5712 #ifdef _WIN32
5713 HANDLE proc;
5714 proc = OpenProcess(PROCESS_TERMINATE, false, pid);
5715 if (proc == NULL) return true; /* Process could not be found. */
5716
5717 if (!TerminateProcess(proc, 201)) killed = false;
5718
5719 CloseHandle(proc);
5720 #else
5721 516 killed = (my_kill(pid, SIGKILL) == 0);
5722 #endif
5723 516 return killed;
5724 }
5725
5726 /**
5727 Abort process.
5728
5729 @param pid Process id.
5730 @param path Path to create minidump file in.
5731 */
5732 24 static void abort_process(int pid, const char *path [[maybe_unused]]) {
5733 #ifdef _WIN32
5734 HANDLE proc;
5735 proc = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
5736 verbose_msg("Aborting pid %d (handle: %p)\n", pid, proc);
5737 if (proc != NULL) {
5738 char name[FN_REFLEN], *end;
5739 BOOL is_debugged;
5740
5741 if (path) {
5742 /* Append mysqld.<pid>.dmp to the path. */
5743 strncpy(name, path, sizeof(name) - 1);
5744 name[sizeof(name) - 1] = '\0';
5745 end = name + std::strlen(name);
5746 /* Make sure "/mysqld.nnnnnnnnnn.dmp" fits */
5747 if ((end - name) < (sizeof(name) - 23)) {
5748 if (!is_directory_separator(end[-1])) {
5749 end[0] = FN_LIBCHAR2; // datadir path normally uses '/'.
5750 end++;
5751 }
5752 snprintf(end, sizeof(name) + name - end - 1, "mysqld.%d.dmp", pid);
5753
5754 verbose_msg("Creating minidump.\n");
5755 my_create_minidump(name, proc, pid);
5756 } else
5757 die("Path too long for creating minidump!\n");
5758 }
5759 /* If running in a debugger, send a break, otherwise terminate. */
5760 if (CheckRemoteDebuggerPresent(proc, &is_debugged) && is_debugged) {
5761 if (!DebugBreakProcess(proc)) {
5762 DWORD err = GetLastError();
5763 verbose_msg("DebugBreakProcess failed: %d\n", err);
5764 } else
5765 verbose_msg("DebugBreakProcess succeeded!\n");
5766 CloseHandle(proc);
5767 } else {
5768 CloseHandle(proc);
5769 (void)kill_process(pid);
5770 }
5771 } else {
5772 DWORD err = GetLastError();
5773 verbose_msg("OpenProcess failed: %d\n", err);
5774 }
5775 #else
5776 24 log_msg("shutdown_server timeout exceeded, SIGABRT set to the server PID %d",
5777 pid);
5778 24 my_kill(pid, SIGABRT);
5779 #endif
5780 24 }
5781
5782 /// Shutdown or kill the server. If timeout is set to 0 the server is
5783 /// killed or terminated immediately. Otherwise the shutdown command
5784 /// is first sent and then it waits for the server to terminate within
5785 /// 'timeout' seconds. If it has not terminated before 'timeout'
5786 /// seconds the command will fail.
5787 ///
5788 /// @note
5789 /// Currently only works with local server
5790 ///
5791 /// @param command Pointer to the st_command structure which holds the
5792 /// arguments and information for the command. Optionally
5793 /// including a timeout else the default of 60 seconds
5794 5214 static void do_shutdown_server(struct st_command *command) {
5795 static DYNAMIC_STRING ds_timeout;
5796 5214 const struct command_arg shutdown_args[] = {
5797 {"timeout", ARG_STRING, false, &ds_timeout,
5798 "Timeout before killing server"}};
5799
5800
1/2
✓ Branch 0 taken 5214 times.
✗ Branch 1 not taken.
5214 check_command_args(command, command->first_argument, shutdown_args,
5801 sizeof(shutdown_args) / sizeof(struct command_arg), ' ');
5802
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5214 times.
5214 if (opt_offload_count_file) {
5803 // Save the value of secondary engine execution status
5804 // before shutting down the server.
5805 if (secondary_engine->offload_count(&cur_con->mysql, "after"))
5806 cleanup_and_exit(1);
5807 }
5808
5809 5214 long timeout = 600;
5810
5811
2/2
✓ Branch 0 taken 4617 times.
✓ Branch 1 taken 597 times.
5214 if (ds_timeout.length) {
5812 char *endptr;
5813 4617 timeout = std::strtol(ds_timeout.str, &endptr, 10);
5814
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4617 times.
4617 if (*endptr != '\0')
5815 die("Illegal argument for timeout: '%s'", ds_timeout.str);
5816 }
5817
5818
1/2
✓ Branch 0 taken 5214 times.
✗ Branch 1 not taken.
5214 dynstr_free(&ds_timeout);
5819
5820 5214 MYSQL *mysql = &cur_con->mysql;
5821 5214 std::string ds_file_name;
5822
5823 // Get the servers pid_file name and use it to read pid
5824
2/4
✓ Branch 0 taken 5214 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5214 times.
5214 if (query_get_string(mysql, "SHOW VARIABLES LIKE 'pid_file'", 1,
5825 &ds_file_name))
5826 die("Failed to get pid_file from server");
5827
5828 // Read the pid from the file
5829 int fd;
5830 char buff[32];
5831
5832
2/4
✓ Branch 0 taken 5214 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5214 times.
5214 if ((fd = my_open(ds_file_name.c_str(), O_RDONLY, MYF(0))) < 0)
5833 die("Failed to open file '%s'", ds_file_name.c_str());
5834
5835
2/4
✓ Branch 0 taken 5214 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5214 times.
5214 if (my_read(fd, (uchar *)&buff, sizeof(buff), MYF(0)) <= 0) {
5836 my_close(fd, MYF(0));
5837 die("pid file was empty");
5838 }
5839
5840
1/2
✓ Branch 0 taken 5214 times.
✗ Branch 1 not taken.
5214 my_close(fd, MYF(0));
5841
5842 5214 int pid = std::atoi(buff);
5843
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5214 times.
5214 if (pid == 0) die("Pidfile didn't contain a valid number");
5844
5845 5214 int error = 0;
5846
2/2
✓ Branch 0 taken 4698 times.
✓ Branch 1 taken 516 times.
5214 if (timeout) {
5847 // Check if we should generate a minidump on timeout.
5848
1/2
✓ Branch 0 taken 4698 times.
✗ Branch 1 not taken.
4698 if (query_get_string(mysql, "SHOW VARIABLES LIKE 'core_file'", 1,
5849
3/4
✓ Branch 0 taken 4698 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4670 times.
✓ Branch 3 taken 28 times.
9396 &ds_file_name) ||
5850
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 4670 times.
4698 std::strcmp("ON", ds_file_name.c_str())) {
5851 } else {
5852 // Get the data dir and use it as path for a minidump if needed.
5853
2/4
✓ Branch 0 taken 4670 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4670 times.
4670 if (query_get_string(mysql, "SHOW VARIABLES LIKE 'datadir'", 1,
5854 &ds_file_name))
5855 die("Failed to get datadir from server");
5856 }
5857
5858 4698 const char *var_name = "$MTR_MANUAL_DEBUG";
5859
1/2
✓ Branch 0 taken 4698 times.
✗ Branch 1 not taken.
4698 VAR *var = var_get(var_name, &var_name, false, false);
5860
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4698 times.
4698 if (var->int_val) {
5861 if (!kill_process(pid) && is_process_active(pid)) error = 3;
5862 } else {
5863 // Tell server to shutdown if timeout > 0.
5864
4/8
✓ Branch 0 taken 4698 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4698 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4698 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4698 times.
4698 if (timeout > 0 && mysql_query_wrapper(mysql, "shutdown")) {
5865 // Failed to issue shutdown command.
5866 error = 1;
5867 4674 goto end;
5868 }
5869
5870 // Check that server dies
5871 do {
5872
3/4
✓ Branch 0 taken 41064 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4674 times.
✓ Branch 3 taken 36390 times.
41064 if (!is_process_active(pid)) {
5873
3/8
✓ Branch 0 taken 4674 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4674 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4674 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
4674 DBUG_PRINT("info", ("Process %d does not exist anymore", pid));
5874 4674 goto end;
5875 }
5876
2/2
✓ Branch 0 taken 36366 times.
✓ Branch 1 taken 24 times.
36390 if (timeout > 0) {
5877
3/8
✓ Branch 0 taken 36366 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36366 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 36366 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
36366 DBUG_PRINT("info", ("Sleeping, timeout: %ld", timeout));
5878
1/2
✓ Branch 0 taken 36366 times.
✗ Branch 1 not taken.
36366 my_sleep(1000000L);
5879 }
5880
2/2
✓ Branch 0 taken 36366 times.
✓ Branch 1 taken 24 times.
36390 } while (timeout-- > 0);
5881
5882 24 error = 2;
5883
5884 // Abort to make it easier to find the hang/problem.
5885
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 abort_process(pid, ds_file_name.c_str());
5886 }
5887 } else {
5888 // timeout value is 0, kill the server
5889
3/8
✓ Branch 0 taken 516 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 516 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 516 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
516 DBUG_PRINT("info", ("Killing server, pid: %d", pid));
5890
5891 // kill_process can fail (bad privileges, non existing process on
5892 // *nix etc), so also check if the process is active before setting
5893 // error.
5894
3/10
✓ Branch 0 taken 516 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 516 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 516 times.
✗ Branch 9 not taken.
516 if (!kill_process(pid) && is_process_active(pid)) error = 3;
5895 }
5896
5897 516 end:
5898
2/4
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 5190 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
5214 if (error) handle_command_error(command, error);
5899 5190 }
5900
5901 /// Evaluate the warning list argument specified with either
5902 /// disable_warnings or enable_warnings command and replace the
5903 /// variables with actual values if there exist any.
5904 ///
5905 /// @param command Pointer to the st_command structure which holds
5906 /// the arguments and information for the command.
5907 /// @param ds_warnings DYNAMIC_STRING object containing the argument.
5908 ///
5909 /// @retval Evaluated string after replacing the variables with values.
5910 1423 const char *eval_warning_argument(struct st_command *command,
5911 DYNAMIC_STRING *ds_warnings) {
5912 1423 dynstr_trunc(ds_warnings, ds_warnings->length);
5913 1423 do_eval(ds_warnings, command->first_argument, command->end, false);
5914 1423 return ds_warnings->str;
5915 }
5916
5917 /// Check if second argument "ONCE" to disable_warnings or enable_warnings
5918 /// command is specified. If yes, filter out the keyword "ONCE" from the
5919 /// argument string.
5920 ///
5921 /// @param ds_property DYNAMIC_STRING object containing the second argument
5922 /// @param warn_argument String to store the argument string containing only
5923 /// the list of warnings.
5924 ///
5925 /// @retval True if the second argument is specified, false otherwise.
5926 1423 static bool check_and_filter_once_property(DYNAMIC_STRING ds_property,
5927 std::string *warn_argument) {
5928
2/2
✓ Branch 0 taken 1266 times.
✓ Branch 1 taken 157 times.
1423 if (ds_property.length) {
5929 // Second argument exists, and it should be "ONCE" keyword.
5930
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1264 times.
1266 if (std::strcmp(ds_property.str, "ONCE"))
5931 2 die("Second argument to '%s' command should always be \"ONCE\" keyword.",
5932 2 command_names[curr_command->type - 1]);
5933
5934 // Filter out the keyword and save only the warnings.
5935 1264 std::size_t position = warn_argument->find(" ONCE");
5936
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1264 times.
1264 assert(position != std::string::npos);
5937 1264 warn_argument->erase(position, 5);
5938 1264 return true;
5939 }
5940
5941 157 return false;
5942 }
5943
5944 /// Handle disabling of warnings.
5945 ///
5946 /// * If all warnings are disabled, don't add the warning to disabled
5947 /// warning list.
5948 /// * If there exist enabled warnings, remove the disabled warning from
5949 /// the list of enabled warnings.
5950 /// * If all the warnings are enabled or if there exist disabled warnings,
5951 /// add or append the new warning to the list of disabled warnings.
5952 ///
5953 /// @param warning_code Warning code
5954 /// @param warning Warning string
5955 /// @param once_prop Flag specifying whether a property should be set
5956 /// for next statement only.
5957 1367 static void handle_disable_warnings(std::uint32_t warning_code,
5958 std::string warning, bool once_prop) {
5959
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1362 times.
1367 if (enabled_warnings->count()) {
5960 // Remove the warning from list of enabled warnings.
5961 5 enabled_warnings->remove_warning(warning_code, once_prop);
5962
4/6
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1357 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1362 times.
✗ Branch 5 not taken.
1362 } else if (!disable_warnings || disabled_warnings->count()) {
5963 // Add the warning to list of expected warnings only if all the
5964 // warnings are not disabled.
5965 1362 disabled_warnings->add_warning(warning_code, warning.c_str(), once_prop);
5966 }
5967 1367 }
5968
5969 /// Handle enabling of warnings.
5970 ///
5971 /// * If all the warnings are enabled, don't add the warning to enabled
5972 /// warning list.
5973 /// * If there exist disabled warnings, remove the enabled warning from
5974 /// the list of disabled warnings.
5975 /// * If all the warnings are disabled or if there exist enabled warnings,
5976 /// add or append the new warning to the list of enabled warnings.
5977 ///
5978 /// @param warning_code Warning code
5979 /// @param warning Warning string
5980 /// @param once_prop Flag specifying whether a property should be set
5981 /// for next statement only.
5982 94 static void handle_enable_warnings(std::uint32_t warning_code,
5983 std::string warning, bool once_prop) {
5984
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 20 times.
94 if (disabled_warnings->count()) {
5985 // Remove the warning from list of disabled warnings.
5986 74 disabled_warnings->remove_warning(warning_code, once_prop);
5987
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 } else if (disabled_warnings) {
5988 // All the warnings are disabled, enable only the warnings specified
5989 // in the argument.
5990 20 enabled_warnings->add_warning(warning_code, warning.c_str(), once_prop);
5991 }
5992 94 }
5993
5994 /// Get an error code corresponding to a warning name. The warning name
5995 /// specified is in symbolic error name format.
5996 ///
5997 /// @param command Pointer to the st_command structure which holds the
5998 /// arguments and information for the command.
5999 /// @param warn_argument String containing warning argument
6000 /// @param once_prop Flag specifying whether a property should be set for
6001 /// next statement only.
6002 1421 static void get_warning_codes(struct st_command *command,
6003 std::string warn_argument, bool once_prop) {
6004 1421 std::string warning;
6005
1/2
✓ Branch 0 taken 1421 times.
✗ Branch 1 not taken.
1421 std::stringstream warnings(warn_argument);
6006
6007
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1419 times.
1421 if (!my_isalnum(charset_info, warn_argument.back())) {
6008 2 die("Invalid argument '%s' to '%s' command.", command->first_argument,
6009 2 command_names[command->type - 1]);
6010 }
6011
6012
4/6
✓ Branch 0 taken 2880 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2880 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1467 times.
✓ Branch 5 taken 1413 times.
2880 while (std::getline(warnings, warning, ',')) {
6013 // Remove any space in a string representing a warning.
6014
2/4
✓ Branch 0 taken 1467 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1467 times.
✗ Branch 3 not taken.
1467 warning.erase(remove_if(warning.begin(), warning.end(), isspace),
6015 1467 warning.end());
6016
6017 // Check if a warning name is a valid symbolic error name.
6018
6/6
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1458 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 1463 times.
✓ Branch 5 taken 4 times.
1467 if (warning.front() == 'E' || warning.front() == 'W') {
6019
2/4
✓ Branch 0 taken 1463 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1463 times.
✗ Branch 3 not taken.
1463 int warning_code = get_errcode_from_name(warning);
6020
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1461 times.
1463 if (warning_code == -1)
6021 2 die("Unknown SQL error name '%s'.", warning.c_str());
6022
2/2
✓ Branch 0 taken 1367 times.
✓ Branch 1 taken 94 times.
1461 if (command->type == Q_DISABLE_WARNINGS)
6023
2/4
✓ Branch 0 taken 1367 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1367 times.
✗ Branch 3 not taken.
1367 handle_disable_warnings(warning_code, warning, once_prop);
6024
1/2
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
94 else if (command->type == Q_ENABLE_WARNINGS) {
6025
2/4
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 94 times.
✗ Branch 3 not taken.
94 handle_enable_warnings(warning_code, warning, once_prop);
6026 // If disable_warnings flag is set, and there are no disabled or
6027 // enabled warnings, set the disable_warnings flag to 0.
6028
1/2
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
94 if (disable_warnings) {
6029
6/6
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 64 times.
✓ Branch 3 taken 20 times.
✓ Branch 4 taken 64 times.
✓ Branch 5 taken 30 times.
94 if (!disabled_warnings->count() && !enabled_warnings->count())
6030
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 set_property(command, P_WARN, false);
6031 }
6032 }
6033 } else {
6034 // Invalid argument, should only consist of warnings specified in
6035 // symbolic error name format.
6036 4 die("Invalid argument '%s' to '%s' command, list of disabled or enabled "
6037 "warnings may only consist of symbolic error names.",
6038 4 command->first_argument, command_names[command->type - 1]);
6039 }
6040 }
6041 1413 }
6042
6043 /// Parse the warning list argument specified with disable_warnings or
6044 /// enable_warnings command. Check if the second argument "ONCE" is
6045 /// specified, if yes, set once_property flag.
6046 ///
6047 /// @param command Pointer to the st_command structure which holds the
6048 /// arguments and information for the command.
6049 ///
6050 /// @retval True if second argument "ONCE" is specified, false otherwise.
6051 1426 static bool parse_warning_list_argument(struct st_command *command) {
6052 DYNAMIC_STRING ds_warnings, ds_property;
6053 1426 const struct command_arg warning_args[] = {
6054 {"Warnings", ARG_STRING, false, &ds_warnings,
6055 "Comma separated list of warnings to be disabled or enabled."},
6056 {"Property", ARG_STRING, false, &ds_property,
6057 "Keyword \"ONCE\" repesenting the property should be set for next "
6058 1426 "statement only."}};
6059
6060
1/2
✓ Branch 0 taken 1426 times.
✗ Branch 1 not taken.
1426 check_command_args(command, command->first_argument, warning_args,
6061 sizeof(warning_args) / sizeof(struct command_arg), ' ');
6062
6063 // Waning list argument can't be an empty string.
6064
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1423 times.
1426 if (!ds_warnings.length)
6065 3 die("Warning list argument to command '%s' can't be an empty string.",
6066 3 command_names[command->type - 1]);
6067
6068 // Evaluate warning list argument and replace the variables with
6069 // actual values
6070
2/4
✓ Branch 0 taken 1423 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1423 times.
✗ Branch 3 not taken.
1423 std::string warn_argument = eval_warning_argument(command, &ds_warnings);
6071
6072 // Set once_prop flag to true if keyword "ONCE" is specified as an
6073 // argument to a disable_warnings or a enable_warnings command.
6074 // Filter this keyword from the argument string and save only the
6075 // list of warnings.
6076
1/2
✓ Branch 0 taken 1421 times.
✗ Branch 1 not taken.
1423 bool once_prop = check_and_filter_once_property(ds_property, &warn_argument);
6077
6078 // Free all the DYNAMIC_STRING objects created
6079
1/2
✓ Branch 0 taken 1421 times.
✗ Branch 1 not taken.
1421 free_dynamic_strings(&ds_warnings, &ds_property);
6080
6081 // Get warning codes
6082
2/4
✓ Branch 0 taken 1421 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1413 times.
✗ Branch 3 not taken.
1421 get_warning_codes(command, warn_argument, once_prop);
6083
6084 1413 return once_prop;
6085 1413 }
6086
6087 /// Create a list of disabled warnings that should be suppressed for
6088 /// the next statements until enabled by enable_warnings command.
6089 ///
6090 /// disable_warnings command can take an optional argument specifying
6091 /// a warning or a comma separated list of warnings to be disabled.
6092 /// The warnings specified should be in symbolic error name format.
6093 ///
6094 /// disable_warnings command can also take a second optional argument,
6095 /// which when specified will suppress the warnings for next statement
6096 /// only. The second argument must be a "ONCE" keyword.
6097 ///
6098 /// @param command Pointer to the st_command structure which holds the
6099 /// arguments and information for the command.
6100 41746 static void do_disable_warnings(struct st_command *command) {
6101 // Revert the previously set properties
6102
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 41745 times.
41746 if (once_property) revert_properties();
6103
6104 // Check if disable_warnings command has warning list argument.
6105
2/2
✓ Branch 0 taken 40410 times.
✓ Branch 1 taken 1336 times.
41746 if (!*command->first_argument) {
6106 // disable_warnings without an argument, disable all the warnings.
6107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40410 times.
40410 if (disabled_warnings->count()) disabled_warnings->clear_list();
6108
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40410 times.
40410 if (enabled_warnings->count()) enabled_warnings->clear_list();
6109
6110 // Update the environment variables containing the list of disabled
6111 // and enabled warnings.
6112 40410 update_disabled_enabled_warnings_list_var();
6113
6114 // Set 'disable_warnings' property value to 1
6115 40410 set_property(command, P_WARN, true);
6116 40410 return;
6117 } else {
6118 // Parse the warning list argument specified with disable_warnings
6119 // command.
6120 1336 parse_warning_list_argument(command);
6121
6122 // Update the environment variables containing the list of disabled
6123 // and enabled warnings.
6124 1330 update_disabled_enabled_warnings_list_var();
6125
6126 // Set 'disable_warnings' property value to 1
6127 1330 set_property(command, P_WARN, true);
6128 }
6129
6130 1330 command->last_argument = command->end;
6131 }
6132
6133 /// Create a list of enabled warnings that should be enabled for the
6134 /// next statements until disabled by disable_warnings command.
6135 ///
6136 /// enable_warnings command can take an optional argument specifying
6137 /// a warning or a comma separated list of warnings to be enabled. The
6138 /// warnings specified should be in symbolic error name format.
6139 ///
6140 /// enable_warnings command can also take a second optional argument,
6141 /// which when specified will enable the warnings for next statement
6142 /// only. The second argument must be a "ONCE" keyword.
6143 ///
6144 /// @param command Pointer to the st_command structure which holds the
6145 /// arguments and information for the command.
6146 34089 static void do_enable_warnings(struct st_command *command) {
6147 // Revert the previously set properties
6148
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 34088 times.
34089 if (once_property) revert_properties();
6149
6150 34089 bool once_prop = false;
6151
2/2
✓ Branch 0 taken 33999 times.
✓ Branch 1 taken 90 times.
34089 if (!*command->first_argument) {
6152 // enable_warnings without an argument, enable all the warnings.
6153
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 33998 times.
33999 if (disabled_warnings->count()) disabled_warnings->clear_list();
6154
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 33996 times.
33999 if (enabled_warnings->count()) enabled_warnings->clear_list();
6155
6156 // Update the environment variables containing the list of disabled
6157 // and enabled warnings.
6158 33999 update_disabled_enabled_warnings_list_var();
6159
6160 // Set 'disable_warnings' property value to 0
6161 33999 set_property(command, P_WARN, false);
6162 } else {
6163 // Parse the warning list argument specified with enable_warnings command.
6164 90 once_prop = parse_warning_list_argument(command);
6165
6166 // Update the environment variables containing the list of disabled and
6167 // enabled warnings.
6168 83 update_disabled_enabled_warnings_list_var();
6169 }
6170
6171 // Call set_once_property() to set once_propetry flag.
6172
4/4
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 34063 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 12 times.
34082 if (disable_warnings && once_prop) set_once_property(P_WARN, true);
6173
6174 34082 command->last_argument = command->end;
6175 34082 }
6176
6177 /// Create a list of error values that the next statement is expected
6178 /// to return. Each error must be an error number or an SQLSTATE value
6179 /// or a symbolic error name representing an error.
6180 ///
6181 /// SQLSTATE value must start with 'S'. It is also possible to specify
6182 /// client errors with 'error' command.
6183 ///
6184 /// @code
6185 /// --error 1064
6186 /// --error S42S01
6187 /// --error ER_TABLE_EXISTS_ERROR,ER_PARSE_ERROR
6188 /// --error CR_SERVER_GONE_ERROR
6189 /// @endcode
6190 ///
6191 /// @param command Pointer to the st_command structure which holds the
6192 /// arguments and information for the command.
6193 1886027 static void do_error(struct st_command *command) {
6194
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1886025 times.
1886027 if (!*command->first_argument) die("Missing argument(s) to 'error' command.");
6195
6196 // Check if error command ends with a comment
6197 1886025 char *end = command->first_argument + std::strlen(command->first_argument);
6198
2/2
✓ Branch 0 taken 229231371 times.
✓ Branch 1 taken 1885978 times.
231117349 while (std::strlen(command->first_argument) > std::strlen(end)) {
6199 229231371 end--;
6200
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 229231324 times.
229231371 if (*end == '#') break;
6201 }
6202
6203
2/2
✓ Branch 0 taken 1885978 times.
✓ Branch 1 taken 47 times.
1886025 if (std::strlen(command->first_argument) == std::strlen(end))
6204 1885978 end = command->first_argument + std::strlen(command->first_argument);
6205
6206 1886025 std::string error;
6207
2/4
✓ Branch 0 taken 1886025 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1886025 times.
✗ Branch 3 not taken.
3772050 std::stringstream errors(std::string(command->first_argument, end));
6208
6209 // Get error codes
6210
4/6
✓ Branch 0 taken 24068287 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24068287 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 22182273 times.
✓ Branch 5 taken 1886014 times.
24068287 while (std::getline(errors, error, ',')) {
6211 // Remove any space from the string representing an error.
6212
2/4
✓ Branch 0 taken 22182273 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22182273 times.
✗ Branch 3 not taken.
22182273 error.erase(remove_if(error.begin(), error.end(), isspace), error.end());
6213
6214 // Code to handle a variable containing an error.
6215
2/2
✓ Branch 0 taken 3900 times.
✓ Branch 1 taken 22178373 times.
22182273 if (error.front() == '$') {
6216 3900 const char *varname_end = nullptr;
6217
1/2
✓ Branch 0 taken 3900 times.
✗ Branch 1 not taken.
3900 VAR *var = var_get(error.c_str(), &varname_end, false, false);
6218
1/2
✓ Branch 0 taken 3900 times.
✗ Branch 1 not taken.
3900 error.assign(var->str_val);
6219 }
6220
6221
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 22182242 times.
22182273 if (error.front() == 'S') {
6222 // SQLSTATE string
6223 // * Must be SQLSTATE_LENGTH long
6224 // * May contain only digits[0-9] and _uppercase_ letters
6225
6226 // Step pass 'S' character
6227
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 error = error.substr(1, error.length());
6228
6229
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 29 times.
31 if (error.length() != SQLSTATE_LENGTH)
6230 2 die("The sqlstate must be exactly %d chars long.", SQLSTATE_LENGTH);
6231
6232 // Check the validity of an SQLSTATE string.
6233
2/2
✓ Branch 0 taken 143 times.
✓ Branch 1 taken 28 times.
171 for (std::size_t i = 0; i < error.length(); i++) {
6234
5/6
✓ Branch 0 taken 143 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 140 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 142 times.
146 if (!my_isdigit(charset_info, error[i]) &&
6235
3/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
3 !my_isupper(charset_info, error[i]))
6236 1 die("The sqlstate may only consist of digits[0-9] and _uppercase_ "
6237 "letters.");
6238 }
6239
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 expected_errors->add_error(error.c_str(), ERR_SQLSTATE);
6240
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 22182241 times.
22182242 } else if (error.front() == 's') {
6241 1 die("The sqlstate definition must start with an uppercase S.");
6242 }
6243 // Checking for both server error names as well as client error names.
6244
6/6
✓ Branch 0 taken 22181300 times.
✓ Branch 1 taken 941 times.
✓ Branch 2 taken 5134297 times.
✓ Branch 3 taken 17047003 times.
✓ Branch 4 taken 5135238 times.
✓ Branch 5 taken 17047003 times.
22182241 else if (error.front() == 'C' || error.front() == 'E') {
6245 // Code to handle --error <error_string>.
6246
2/4
✓ Branch 0 taken 5135238 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5135238 times.
✗ Branch 3 not taken.
5135238 int error_code = get_errcode_from_name(error);
6247
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5135237 times.
5135238 if (error_code == -1) die("Unknown SQL error name '%s'.", error.c_str());
6248
1/2
✓ Branch 0 taken 5135237 times.
✗ Branch 1 not taken.
5135237 expected_errors->add_error(error_code, ERR_ERRNO);
6249
5/6
✓ Branch 0 taken 17047003 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 17047002 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 17047002 times.
17047003 } else if (error.front() == 'c' || error.front() == 'e') {
6250 1 die("The error name definition must start with an uppercase C or E.");
6251 } else {
6252 // Check that the string passed to error command contains only digits.
6253
1/2
✓ Branch 0 taken 17047002 times.
✗ Branch 1 not taken.
17047002 int error_code = get_int_val(error.c_str());
6254
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 17046998 times.
17047002 if (error_code == -1)
6255 4 die("Invalid argument '%s' to 'error' command, the argument may "
6256 "consist of either symbolic error names or error codes.",
6257 command->first_argument);
6258
1/2
✓ Branch 0 taken 17046998 times.
✗ Branch 1 not taken.
17046998 expected_errors->add_error((std::uint32_t)error_code, ERR_ERRNO);
6259 }
6260
6261
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 22182262 times.
22182263 if (expected_errors->count() >= MAX_ERROR_COUNT)
6262 1 die("Too many errorcodes specified.");
6263 }
6264
6265 1886014 command->last_argument = command->end;
6266 1886014 }
6267
6268 /*
6269 Get a string; Return ptr to end of string
6270 Strings may be surrounded by " or '
6271
6272 If string is a '$variable', return the value of the variable.
6273 */
6274
6275 399489 static char *get_string(char **to_ptr, const char **from_ptr,
6276 struct st_command *command) {
6277 char c, sep;
6278 399489 char *to = *to_ptr, *start = to;
6279 399489 const char *from = *from_ptr;
6280
1/2
✓ Branch 0 taken 399489 times.
✗ Branch 1 not taken.
399489 DBUG_TRACE;
6281
6282 /* Find separator */
6283
4/4
✓ Branch 0 taken 367426 times.
✓ Branch 1 taken 32063 times.
✓ Branch 2 taken 951 times.
✓ Branch 3 taken 366475 times.
399489 if (*from == '"' || *from == '\'')
6284 33014 sep = *from++;
6285 else
6286 366475 sep = ' '; /* Separated with space */
6287
6288
2/2
✓ Branch 0 taken 1858986 times.
✓ Branch 1 taken 33052 times.
1892038 for (; (c = *from); from++) {
6289
3/4
✓ Branch 0 taken 596 times.
✓ Branch 1 taken 1858390 times.
✓ Branch 2 taken 596 times.
✗ Branch 3 not taken.
1858986 if (c == '\\' && from[1]) { /* Escaped character */
6290 /* We can't translate \0 -> ASCII 0 as replace can't handle ASCII 0 */
6291
3/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 414 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 178 times.
596 switch (*++from) {
6292 4 case 'n':
6293 4 *to++ = '\n';
6294 4 break;
6295 case 't':
6296 *to++ = '\t';
6297 break;
6298 414 case 'r':
6299 414 *to++ = '\r';
6300 414 break;
6301 case 'b':
6302 *to++ = '\b';
6303 break;
6304 case 'Z': /* ^Z must be escaped on Win32 */
6305 *to++ = '\032';
6306 break;
6307 178 default:
6308 178 *to++ = *from;
6309 178 break;
6310 }
6311
2/2
✓ Branch 0 taken 366437 times.
✓ Branch 1 taken 1491953 times.
1858390 } else if (c == sep) {
6312
4/6
✓ Branch 0 taken 33014 times.
✓ Branch 1 taken 333423 times.
✓ Branch 2 taken 33014 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 366437 times.
✗ Branch 5 not taken.
366437 if (c == ' ' || c != *++from) break; /* Found end of string */
6313 *to++ = c; /* Copy duplicated separator */
6314 } else
6315 1491953 *to++ = c;
6316 }
6317
3/4
✓ Branch 0 taken 49371 times.
✓ Branch 1 taken 350118 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49371 times.
399489 if (*from != ' ' && *from) die("Wrong string argument in %s", command->query);
6318
6319
2/2
✓ Branch 0 taken 351660 times.
✓ Branch 1 taken 399489 times.
751149 while (my_isspace(charset_info, *from)) /* Point to next string */
6320 351660 from++;
6321
6322 399489 *to = 0; /* End of string marker */
6323 399489 *to_ptr = to + 1; /* Store pointer to end */
6324 399489 *from_ptr = from;
6325
6326 /* Check if this was a variable */
6327
2/2
✓ Branch 0 taken 41382 times.
✓ Branch 1 taken 358107 times.
399489 if (*start == '$') {
6328 41382 const char *end = to;
6329
1/2
✓ Branch 0 taken 41382 times.
✗ Branch 1 not taken.
41382 VAR *var = var_get(start, &end, false, true);
6330
3/4
✓ Branch 0 taken 41382 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 41081 times.
✓ Branch 3 taken 301 times.
41382 if (var && to == end + 1) {
6331
3/8
✓ Branch 0 taken 41081 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 41081 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 41081 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
41081 DBUG_PRINT("info", ("var: '%s' -> '%s'", start, var->str_val));
6332 41081 return var->str_val; /* return found variable value */
6333 }
6334 }
6335 358408 return start;
6336 399489 }
6337
6338 25786 static void set_reconnect(MYSQL *mysql, int val) {
6339 25786 bool reconnect = val;
6340
1/2
✓ Branch 0 taken 25786 times.
✗ Branch 1 not taken.
25786 DBUG_TRACE;
6341
3/8
✓ Branch 0 taken 25786 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25786 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 25786 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
25786 DBUG_PRINT("info", ("val: %d", val));
6342
1/2
✓ Branch 0 taken 25786 times.
✗ Branch 1 not taken.
25786 mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
6343 25786 }
6344
6345 /**
6346 Change the current connection to the given st_connection, and update
6347 $mysql_get_server_version and $CURRENT_CONNECTION accordingly.
6348 */
6349 1223885 static void set_current_connection(struct st_connection *con) {
6350 1223885 cur_con = con;
6351 /* Update $mysql_get_server_version to that of current connection */
6352 1223885 var_set_int("$mysql_get_server_version",
6353 1223885 mysql_get_server_version(&con->mysql));
6354 /* Update $CURRENT_CONNECTION to the name of the current connection */
6355 1223885 var_set_string("$CURRENT_CONNECTION", con->name);
6356 1223885 }
6357
6358 1121992 static void select_connection_name(const char *name) {
6359
1/2
✓ Branch 0 taken 1121992 times.
✗ Branch 1 not taken.
1121992 DBUG_TRACE;
6360
3/8
✓ Branch 0 taken 1121992 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1121992 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1121992 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1121992 DBUG_PRINT("enter", ("name: '%s'", name));
6361 1121992 st_connection *con = find_connection_by_name(name);
6362
6363
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1121991 times.
1121992 if (!con) die("connection '%s' not found in connection pool", name);
6364
6365
1/2
✓ Branch 0 taken 1121991 times.
✗ Branch 1 not taken.
1121991 set_current_connection(con);
6366
6367 /* Connection logging if enabled */
6368
4/4
✓ Branch 0 taken 4763 times.
✓ Branch 1 taken 1117228 times.
✓ Branch 2 taken 4727 times.
✓ Branch 3 taken 36 times.
1121991 if (!disable_connect_log && !disable_query_log) {
6369 4727 DYNAMIC_STRING *ds = &ds_res;
6370
6371
1/2
✓ Branch 0 taken 4727 times.
✗ Branch 1 not taken.
4727 dynstr_append_mem(ds, "connection ", 11);
6372
1/2
✓ Branch 0 taken 4727 times.
✗ Branch 1 not taken.
4727 replace_dynstr_append(ds, name);
6373
1/2
✓ Branch 0 taken 4727 times.
✗ Branch 1 not taken.
4727 dynstr_append_mem(ds, ";\n", 2);
6374 }
6375 1121991 }
6376
6377 1120212 static void select_connection(struct st_command *command) {
6378
1/2
✓ Branch 0 taken 1120212 times.
✗ Branch 1 not taken.
1120212 DBUG_TRACE;
6379 static DYNAMIC_STRING ds_connection;
6380 1120212 const struct command_arg connection_args[] = {
6381 {"connection_name", ARG_STRING, true, &ds_connection,
6382 "Name of the connection that we switch to."}};
6383
1/2
✓ Branch 0 taken 1120212 times.
✗ Branch 1 not taken.
1120212 check_command_args(command, command->first_argument, connection_args,
6384 sizeof(connection_args) / sizeof(struct command_arg), ' ');
6385
6386
3/8
✓ Branch 0 taken 1120212 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1120212 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1120212 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1120212 DBUG_PRINT("info", ("changing connection: %s", ds_connection.str));
6387
1/2
✓ Branch 0 taken 1120211 times.
✗ Branch 1 not taken.
1120212 select_connection_name(ds_connection.str);
6388
1/2
✓ Branch 0 taken 1120211 times.
✗ Branch 1 not taken.
1120211 dynstr_free(&ds_connection);
6389 1120211 }
6390
6391 32744 static void do_close_connection(struct st_command *command) {
6392
1/2
✓ Branch 0 taken 32744 times.
✗ Branch 1 not taken.
32744 DBUG_TRACE;
6393
6394 struct st_connection *con;
6395 static DYNAMIC_STRING ds_connection;
6396 32744 const struct command_arg close_connection_args[] = {
6397 {"connection_name", ARG_STRING, true, &ds_connection,
6398 "Name of the connection to close."}};
6399
1/2
✓ Branch 0 taken 32742 times.
✗ Branch 1 not taken.
32744 check_command_args(command, command->first_argument, close_connection_args,
6400 sizeof(close_connection_args) / sizeof(struct command_arg),
6401 ' ');
6402
6403
3/8
✓ Branch 0 taken 32742 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 32742 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 32742 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
32742 DBUG_PRINT("enter", ("connection name: '%s'", ds_connection.str));
6404
6405
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32742 times.
32742 if (!(con = find_connection_by_name(ds_connection.str)))
6406 die("connection '%s' not found in connection pool", ds_connection.str);
6407
6408
3/8
✓ Branch 0 taken 32742 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 32742 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 32742 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
32742 DBUG_PRINT("info", ("Closing connection %s", con->name));
6409
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 32734 times.
32742 if (command->type == Q_DIRTY_CLOSE) {
6410
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (con->mysql.net.vio) {
6411
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 vio_delete(con->mysql.net.vio);
6412 8 con->mysql.net.vio = nullptr;
6413
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 end_server(&con->mysql);
6414 }
6415 }
6416
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 32742 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
32742 if (con->stmt) mysql_stmt_close(con->stmt);
6417 32742 con->stmt = nullptr;
6418
6419
1/2
✓ Branch 0 taken 32742 times.
✗ Branch 1 not taken.
32742 mysql_close(&con->mysql);
6420
6421
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 32742 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
32742 if (con->util_mysql) mysql_close(con->util_mysql);
6422 32742 con->util_mysql = nullptr;
6423 32742 con->pending = false;
6424
6425
1/2
✓ Branch 0 taken 32742 times.
✗ Branch 1 not taken.
32742 my_free(con->name);
6426
6427 /*
6428 When the connection is closed set name to "-closed_connection-"
6429 to make it possible to reuse the connection name.
6430 */
6431
2/4
✓ Branch 0 taken 32742 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 32742 times.
32742 if (!(con->name = my_strdup(PSI_NOT_INSTRUMENTED, "-closed_connection-",
6432 MYF(MY_WME))))
6433 die("Out of memory");
6434
6435
2/2
✓ Branch 0 taken 6381 times.
✓ Branch 1 taken 26361 times.
32742 if (con == cur_con) {
6436 /* Current connection was closed */
6437
1/2
✓ Branch 0 taken 6381 times.
✗ Branch 1 not taken.
6381 var_set_int("$mysql_get_server_version", 0xFFFFFFFF);
6438
1/2
✓ Branch 0 taken 6381 times.
✗ Branch 1 not taken.
6381 var_set_string("$CURRENT_CONNECTION", con->name);
6439 }
6440
6441 /* Connection logging if enabled */
6442
4/4
✓ Branch 0 taken 914 times.
✓ Branch 1 taken 31828 times.
✓ Branch 2 taken 913 times.
✓ Branch 3 taken 1 times.
32742 if (!disable_connect_log && !disable_query_log) {
6443 913 DYNAMIC_STRING *ds = &ds_res;
6444
6445
1/2
✓ Branch 0 taken 913 times.
✗ Branch 1 not taken.
913 dynstr_append_mem(ds, "disconnect ", 11);
6446
1/2
✓ Branch 0 taken 913 times.
✗ Branch 1 not taken.
913 replace_dynstr_append(ds, ds_connection.str);
6447
1/2
✓ Branch 0 taken 913 times.
✗ Branch 1 not taken.
913 dynstr_append_mem(ds, ";\n", 2);
6448 }
6449
6450
1/2
✓ Branch 0 taken 32742 times.
✗ Branch 1 not taken.
32742 dynstr_free(&ds_connection);
6451 32742 }
6452
6453 /*
6454 Connect to a server doing several retries if needed.
6455
6456 SYNOPSIS
6457 safe_connect()
6458 con - connection structure to be used
6459 host, user, pass, - connection parameters
6460 db, port, sock
6461
6462 NOTE
6463
6464 Sometimes in a test the client starts before
6465 the server - to solve the problem, we try again
6466 after some sleep if connection fails the first
6467 time
6468
6469 This function will try to connect to the given server
6470 "opt_max_connect_retries" times and sleep "connection_retry_sleep"
6471 seconds between attempts before finally giving up.
6472 This helps in situation when the client starts
6473 before the server (which happens sometimes).
6474 It will only ignore connection errors during these retries.
6475
6476 */
6477
6478 55506 static void safe_connect(MYSQL *mysql, const char *name, const char *host,
6479 const char *user, const char *pass, const char *db,
6480 int port, const char *sock) {
6481 55506 int failed_attempts = 0;
6482
6483
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 DBUG_TRACE;
6484
6485
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 verbose_msg(
6486 "Connecting to server %s:%d (socket %s) as '%s'"
6487 ", connection '%s', attempt %d ...",
6488 host, port, sock, user, name, failed_attempts);
6489
6490
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, nullptr);
6491
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "program_name",
6492 "mysqltest");
6493
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 mysql_options(mysql, MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS,
6494 &can_handle_expired_passwords);
6495
3/4
✓ Branch 0 taken 74506 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19044 times.
✓ Branch 3 taken 55462 times.
74506 while (!mysql_real_connect_wrapper(
6496 mysql, host, user, pass, db, port, sock,
6497 CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS)) {
6498 /*
6499 Connect failed
6500
6501 Only allow retry if this was an error indicating the server
6502 could not be contacted. Error code differs depending
6503 on protocol/connection type
6504 */
6505
6506
1/2
✓ Branch 0 taken 19044 times.
✗ Branch 1 not taken.
19044 if ((mysql_errno(mysql) == CR_CONN_HOST_ERROR ||
6507
3/4
✓ Branch 0 taken 19044 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 19038 times.
19044 mysql_errno(mysql) == CR_CONNECTION_ERROR ||
6508
5/8
✓ Branch 0 taken 19044 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 19000 times.
✓ Branch 7 taken 44 times.
38088 mysql_errno(mysql) == CR_NAMEDPIPEOPEN_ERROR) &&
6509
2/2
✓ Branch 0 taken 19000 times.
✓ Branch 1 taken 38 times.
19038 failed_attempts < opt_max_connect_retries) {
6510
3/6
✓ Branch 0 taken 19000 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19000 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19000 times.
✗ Branch 5 not taken.
19000 verbose_msg("Connect attempt %d/%d failed: %d: %s", failed_attempts,
6511 opt_max_connect_retries, mysql_errno(mysql),
6512 mysql_error(mysql));
6513
1/2
✓ Branch 0 taken 19000 times.
✗ Branch 1 not taken.
19000 my_sleep(connection_retry_sleep);
6514 } else {
6515
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 6 times.
44 if (failed_attempts > 0)
6516
2/4
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 die("Could not open connection '%s' after %d attempts: %d %s", name,
6517 failed_attempts, mysql_errno(mysql), mysql_error(mysql));
6518 else
6519
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 die("Could not open connection '%s': %d %s", name, mysql_errno(mysql),
6520 mysql_error(mysql));
6521 }
6522 19000 failed_attempts++;
6523 }
6524
1/2
✓ Branch 0 taken 55462 times.
✗ Branch 1 not taken.
55462 verbose_msg("... Connected.");
6525 55462 }
6526
6527 /// Connect to a server and handle connection errors in case they
6528 /// occur.
6529 ///
6530 /// This function will try to establish a connection to server and
6531 /// handle possible errors in the same manner as if "connect" was usual
6532 /// SQL-statement. If an error is expected it will ignore it once it
6533 /// occurs and log the "statement" to the query log. Unlike
6534 /// safe_connect() it won't do several attempts.
6535 ///
6536 /// @param command Pointer to the st_command structure which holds the
6537 /// arguments and information for the command.
6538 /// @param con Connection structure to be used
6539 /// @param host Host name
6540 /// @param user User name
6541 /// @param pass Password
6542 /// @param db Database name
6543 /// @param port Port number
6544 /// @param sock Socket value
6545 ///
6546 /// @retval 1 if connection succeeds, 0 otherwise
6547 47120 static int connect_n_handle_errors(struct st_command *command, MYSQL *con,
6548 const char *host, const char *user,
6549 const char *pass, const char *db, int port,
6550 const char *sock) {
6551 DYNAMIC_STRING *ds;
6552 47120 int failed_attempts = 0;
6553
6554 47120 ds = &ds_res;
6555
6556 /* Only log if an error is expected */
6557
6/6
✓ Branch 0 taken 583 times.
✓ Branch 1 taken 46537 times.
✓ Branch 2 taken 240 times.
✓ Branch 3 taken 343 times.
✓ Branch 4 taken 240 times.
✓ Branch 5 taken 46880 times.
47120 if (expected_errors->count() > 0 && !disable_query_log) {
6558 /*
6559 Log the connect to result log
6560 */
6561 240 dynstr_append_mem(ds, "connect(", 8);
6562 240 replace_dynstr_append(ds, host);
6563 240 dynstr_append_mem(ds, ",", 1);
6564 240 replace_dynstr_append(ds, user);
6565 240 dynstr_append_mem(ds, ",", 1);
6566 240 replace_dynstr_append(ds, pass);
6567 240 dynstr_append_mem(ds, ",", 1);
6568
1/2
✓ Branch 0 taken 240 times.
✗ Branch 1 not taken.
240 if (db) replace_dynstr_append(ds, db);
6569 240 dynstr_append_mem(ds, ",", 1);
6570 240 replace_dynstr_append_uint(ds, port);
6571 240 dynstr_append_mem(ds, ",", 1);
6572
1/2
✓ Branch 0 taken 240 times.
✗ Branch 1 not taken.
240 if (sock) replace_dynstr_append(ds, sock);
6573 240 dynstr_append_mem(ds, ")", 1);
6574 240 dynstr_append_mem(ds, delimiter, delimiter_length);
6575 240 dynstr_append_mem(ds, "\n", 1);
6576 }
6577 /* Simplified logging if enabled */
6578
4/4
✓ Branch 0 taken 937 times.
✓ Branch 1 taken 46183 times.
✓ Branch 2 taken 936 times.
✓ Branch 3 taken 1 times.
47120 if (!disable_connect_log && !disable_query_log) {
6579 936 replace_dynstr_append(ds, command->query);
6580 936 dynstr_append_mem(ds, ";\n", 2);
6581 }
6582
6583 47120 mysql_options(con, MYSQL_OPT_CONNECT_ATTR_RESET, nullptr);
6584 47120 mysql_options4(con, MYSQL_OPT_CONNECT_ATTR_ADD, "program_name", "mysqltest");
6585 47120 mysql_options(con, MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS,
6586 &can_handle_expired_passwords);
6587
2/2
✓ Branch 0 taken 686 times.
✓ Branch 1 taken 46434 times.
47120 while (!mysql_real_connect_wrapper(con, host, user, pass, db, port,
6588 sock ? sock : nullptr,
6589 CLIENT_MULTI_STATEMENTS)) {
6590 /*
6591 If we have used up all our connections check whether this
6592 is expected (by --error). If so, handle the error right away.
6593 Otherwise, give it some extra time to rule out race-conditions.
6594 If extra-time doesn't help, we have an unexpected error and
6595 must abort -- just proceeding to handle_error() when second
6596 and third chances are used up will handle that for us.
6597
6598 There are various user-limits of which only max_user_connections
6599 and max_connections_per_hour apply at connect time. For the
6600 the second to create a race in our logic, we'd need a limits
6601 test that runs without a FLUSH for longer than an hour, so we'll
6602 stay clear of trying to work out which exact user-limit was
6603 exceeded.
6604 */
6605
6606 686 if (((mysql_errno(con) == ER_TOO_MANY_USER_CONNECTIONS) ||
6607
6/6
✓ Branch 0 taken 680 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 667 times.
✓ Branch 4 taken 19 times.
✓ Branch 5 taken 667 times.
705 (mysql_errno(con) == ER_USER_LIMIT_REACHED)) &&
6608
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 (failed_attempts++ < opt_max_connect_retries)) {
6609 int i;
6610
6611 19 i = match_expected_error(command, mysql_errno(con), mysql_sqlstate(con));
6612
6613
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if (i >= 0) goto do_handle_error; /* expected error, handle */
6614
6615 my_sleep(connection_retry_sleep); /* unexpected error, wait */
6616 continue; /* and give it 1 more chance */
6617 }
6618
6619 667 do_handle_error:
6620 686 var_set_errno(mysql_errno(con));
6621 686 handle_error(command, mysql_errno(con), mysql_error(con),
6622 mysql_sqlstate(con), ds);
6623 685 return 0; /* Not connected */
6624 }
6625
6626 46434 var_set_errno(0);
6627 46434 handle_no_error(command);
6628 46434 revert_properties();
6629 46434 return 1; /* Connected */
6630 }
6631
6632 /*
6633 Open a new connection to MySQL Server with the parameters
6634 specified. Make the new connection the current connection.
6635
6636 SYNOPSIS
6637 do_connect()
6638 q called command
6639
6640 DESCRIPTION
6641 connect(<name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]]);
6642 connect <name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]];
6643
6644 <name> - name of the new connection
6645 <host> - hostname of server
6646 <user> - user to connect as
6647 <pass> - password used when connecting
6648 <db> - initial db when connected
6649 <port> - server port
6650 <sock> - server socket
6651 <opts> - options to use for the connection
6652 * SSL - use SSL if available
6653 * COMPRESS - use compression if available
6654 * SHM - use shared memory if available
6655 * PIPE - use named pipe if available
6656 * SOCKET - use socket protocol
6657 * TCP - use tcp protocol
6658 */
6659
6660 47129 static void do_connect(struct st_command *command) {
6661 47129 int con_port = opt_port;
6662 char *con_options;
6663 47129 bool con_ssl = false, con_compress = false;
6664 47129 bool con_pipe = false, con_shm = false, con_cleartext_enable = false;
6665 struct st_connection *con_slot;
6666 47129 uint save_opt_ssl_mode = opt_ssl_mode;
6667
6668 static DYNAMIC_STRING ds_connection_name;
6669 static DYNAMIC_STRING ds_host;
6670 static DYNAMIC_STRING ds_user;
6671 static DYNAMIC_STRING ds_password1;
6672 static DYNAMIC_STRING ds_database;
6673 static DYNAMIC_STRING ds_port;
6674 static DYNAMIC_STRING ds_sock;
6675 static DYNAMIC_STRING ds_options;
6676 static DYNAMIC_STRING ds_default_auth;
6677 static DYNAMIC_STRING ds_shm;
6678 static DYNAMIC_STRING ds_compression_algorithm;
6679 static DYNAMIC_STRING ds_zstd_compression_level;
6680 static DYNAMIC_STRING ds_password2;
6681 static DYNAMIC_STRING ds_password3;
6682 47129 const struct command_arg connect_args[] = {
6683 {"connection name", ARG_STRING, true, &ds_connection_name,
6684 "Name of the connection"},
6685 {"host", ARG_STRING, true, &ds_host, "Host to connect to"},
6686 {"user", ARG_STRING, false, &ds_user, "User to connect as"},
6687 {"passsword", ARG_STRING, false, &ds_password1,
6688 "Password used when connecting"},
6689 {"database", ARG_STRING, false, &ds_database,
6690 "Database to select after connect"},
6691 {"port", ARG_STRING, false, &ds_port, "Port to connect to"},
6692 {"socket", ARG_STRING, false, &ds_sock, "Socket to connect with"},
6693 {"options", ARG_STRING, false, &ds_options,
6694 "Options to use while connecting"},
6695 {"default_auth", ARG_STRING, false, &ds_default_auth,
6696 "Default authentication to use"},
6697 {"default_compression_algorithm", ARG_STRING, false,
6698 &ds_compression_algorithm, "Default compression algorithm to use"},
6699 {"default_zstd_compression_level", ARG_STRING, false,
6700 &ds_zstd_compression_level,
6701 "Default compression level to use "
6702 "when using zstd compression."},
6703 {"second_passsword", ARG_STRING, false, &ds_password2,
6704 "Password used when connecting"},
6705 {"third_passsword", ARG_STRING, false, &ds_password3,
6706 "Password used when connecting"}};
6707
6708
1/2
✓ Branch 0 taken 47129 times.
✗ Branch 1 not taken.
47129 DBUG_TRACE;
6709
3/8
✓ Branch 0 taken 47129 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 47129 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 47129 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
47129 DBUG_PRINT("enter", ("connect: %s", command->first_argument));
6710
6711
1/2
✓ Branch 0 taken 47129 times.
✗ Branch 1 not taken.
47129 strip_parentheses(command);
6712
1/2
✓ Branch 0 taken 47125 times.
✗ Branch 1 not taken.
47129 check_command_args(command, command->first_argument, connect_args,
6713 sizeof(connect_args) / sizeof(struct command_arg), ',');
6714
6715 /* Port */
6716
2/2
✓ Branch 0 taken 32403 times.
✓ Branch 1 taken 14722 times.
47125 if (ds_port.length) {
6717 32403 con_port = atoi(ds_port.str);
6718
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 32402 times.
32403 if (con_port == 0) die("Illegal argument for port: '%s'", ds_port.str);
6719 }
6720
6721 /* Shared memory */
6722
1/2
✓ Branch 0 taken 47124 times.
✗ Branch 1 not taken.
47124 init_dynamic_string(&ds_shm, ds_sock.str, 0);
6723
6724 /* Sock */
6725
2/2
✓ Branch 0 taken 197 times.
✓ Branch 1 taken 46927 times.
47124 if (ds_sock.length) {
6726 /*
6727 If the socket is specified just as a name without path
6728 append tmpdir in front
6729 */
6730
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 191 times.
197 if (*ds_sock.str != FN_LIBCHAR) {
6731 char buff[FN_REFLEN];
6732
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 fn_format(buff, ds_sock.str, TMPDIR, "", 0);
6733
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 dynstr_set(&ds_sock, buff);
6734 }
6735 } else {
6736 /* No socket specified, use default */
6737
1/2
✓ Branch 0 taken 46927 times.
✗ Branch 1 not taken.
46927 dynstr_set(&ds_sock, unix_sock);
6738 }
6739
3/8
✓ Branch 0 taken 47124 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 47124 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 47124 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
47124 DBUG_PRINT("info", ("socket: %s", ds_sock.str));
6740
6741 /* Options */
6742 47124 con_options = ds_options.str;
6743 47124 bool con_socket = false, con_tcp = false;
6744
2/2
✓ Branch 0 taken 4489 times.
✓ Branch 1 taken 47121 times.
51610 while (*con_options) {
6745 /* Step past any spaces in beginning of option */
6746
3/4
✓ Branch 0 taken 4513 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✓ Branch 3 taken 4489 times.
4513 while (*con_options && my_isspace(charset_info, *con_options))
6747 24 con_options++;
6748
6749 /* Find end of this option */
6750 4489 char *end = con_options;
6751
4/4
✓ Branch 0 taken 13707 times.
✓ Branch 1 taken 4463 times.
✓ Branch 2 taken 13681 times.
✓ Branch 3 taken 26 times.
18170 while (*end && !my_isspace(charset_info, *end)) end++;
6752
6753 4489 size_t con_option_len = end - con_options;
6754 4489 char cur_con_option[10] = {};
6755
1/2
✓ Branch 0 taken 4489 times.
✗ Branch 1 not taken.
4489 strmake(cur_con_option, con_options, con_option_len);
6756
6757
2/2
✓ Branch 0 taken 4422 times.
✓ Branch 1 taken 67 times.
4489 if (!std::strcmp(cur_con_option, "SSL"))
6758 4422 con_ssl = true;
6759
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 35 times.
67 else if (!std::strcmp(cur_con_option, "COMPRESS"))
6760 32 con_compress = true;
6761
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 34 times.
35 else if (!std::strcmp(cur_con_option, "PIPE"))
6762 1 con_pipe = true;
6763
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 33 times.
34 else if (!std::strcmp(cur_con_option, "SHM"))
6764 1 con_shm = true;
6765
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 26 times.
33 else if (!std::strcmp(cur_con_option, "CLEARTEXT"))
6766 7 con_cleartext_enable = true;
6767
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 25 times.
26 else if (!std::strcmp(cur_con_option, "SOCKET"))
6768 1 con_socket = true;
6769
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 3 times.
25 else if (!std::strcmp(cur_con_option, "TCP"))
6770 22 con_tcp = true;
6771 else
6772 3 die("Illegal option to connect: %s", cur_con_option);
6773
6774 /* Process next option */
6775 4486 con_options = end;
6776 }
6777
6778
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 47120 times.
47121 if (find_connection_by_name(ds_connection_name.str))
6779 1 die("Connection %s already exists", ds_connection_name.str);
6780
6781
2/2
✓ Branch 0 taken 44142 times.
✓ Branch 1 taken 2978 times.
47120 if (next_con != connections_end)
6782 44142 con_slot = next_con;
6783 else {
6784
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2978 times.
2978 if (!(con_slot = find_connection_by_name("-closed_connection-")))
6785 die("Connection limit exhausted, you can have max %d connections",
6786 opt_max_connections);
6787 }
6788
6789
2/4
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 47120 times.
47120 if (!mysql_init(&con_slot->mysql)) die("Failed on mysql_init()");
6790
6791
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47120 times.
47120 if (opt_init_command)
6792 mysql_options(&con_slot->mysql, MYSQL_INIT_COMMAND, opt_init_command);
6793
1/2
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
47120 if (opt_connect_timeout)
6794
1/2
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
47120 mysql_options(&con_slot->mysql, MYSQL_OPT_CONNECT_TIMEOUT,
6795 (void *)&opt_connect_timeout);
6796
6797
4/4
✓ Branch 0 taken 47119 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 47087 times.
47120 if (opt_compress || con_compress)
6798
1/2
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
33 mysql_options(&con_slot->mysql, MYSQL_OPT_COMPRESS, NullS);
6799
1/2
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
47120 mysql_options(&con_slot->mysql, MYSQL_OPT_LOCAL_INFILE, nullptr);
6800
1/2
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
47120 mysql_options(&con_slot->mysql, MYSQL_SET_CHARSET_NAME, charset_info->csname);
6801
1/2
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
47120 if (opt_charsets_dir)
6802
1/2
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
47120 mysql_options(&con_slot->mysql, MYSQL_SET_CHARSET_DIR, opt_charsets_dir);
6803
6804
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 47074 times.
47120 if (ds_compression_algorithm.length)
6805 46 mysql_options(&con_slot->mysql, MYSQL_OPT_COMPRESSION_ALGORITHMS,
6806
1/2
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
46 ds_compression_algorithm.str);
6807
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47074 times.
47074 else if (opt_compress_algorithm)
6808 mysql_options(&con_slot->mysql, MYSQL_OPT_COMPRESSION_ALGORITHMS,
6809 opt_compress_algorithm);
6810
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 47111 times.
47120 if (ds_zstd_compression_level.length) {
6811 9 char *end = nullptr;
6812 9 opt_zstd_compress_level = strtol(ds_zstd_compression_level.str, &end, 10);
6813 }
6814
1/2
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
47120 mysql_options(&con_slot->mysql, MYSQL_OPT_ZSTD_COMPRESSION_LEVEL,
6815 &opt_zstd_compress_level);
6816
6817 /*
6818 If mysqltest --ssl-mode option is set to DISABLED
6819 and connect(.., SSL) command used, set proper opt_ssl_mode.
6820
6821 So, SSL connection is used either:
6822 a) mysqltest --ssl-mode option is NOT set to DISABLED or
6823 b) connect(.., SSL) command used.
6824 */
6825
4/4
✓ Branch 0 taken 46925 times.
✓ Branch 1 taken 195 times.
✓ Branch 2 taken 4350 times.
✓ Branch 3 taken 42575 times.
47120 if (opt_ssl_mode == SSL_MODE_DISABLED && con_ssl) {
6826 4350 opt_ssl_mode =
6827
2/4
✓ Branch 0 taken 4350 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4350 times.
4350 (opt_ssl_ca || opt_ssl_capath) ? SSL_MODE_VERIFY_CA : SSL_MODE_REQUIRED;
6828 }
6829
2/4
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 47120 times.
47120 if (SSL_SET_OPTIONS(&con_slot->mysql)) die("%s", SSL_SET_OPTIONS_ERROR);
6830 47120 opt_ssl_mode = save_opt_ssl_mode;
6831
6832
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 47119 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
47120 if (con_pipe && !con_ssl) {
6833 1 opt_protocol = MYSQL_PROTOCOL_PIPE;
6834 }
6835
6836
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 47119 times.
47120 if (opt_protocol) {
6837
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 mysql_options(&con_slot->mysql, MYSQL_OPT_PROTOCOL, (char *)&opt_protocol);
6838 /*
6839 Resetting the opt_protocol value to 0 to avoid the
6840 possible failure in the next connect() command.
6841 */
6842 1 opt_protocol = 0;
6843 }
6844
6845
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 47119 times.
47120 if (con_shm) {
6846 1 uint protocol = MYSQL_PROTOCOL_MEMORY;
6847
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!ds_shm.length) die("Missing shared memory base name");
6848
6849
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 mysql_options(&con_slot->mysql, MYSQL_SHARED_MEMORY_BASE_NAME, ds_shm.str);
6850
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 mysql_options(&con_slot->mysql, MYSQL_OPT_PROTOCOL, &protocol);
6851
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47119 times.
47119 } else if (shared_memory_base_name) {
6852 mysql_options(&con_slot->mysql, MYSQL_SHARED_MEMORY_BASE_NAME,
6853 shared_memory_base_name);
6854 }
6855
6856
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 47119 times.
47120 if (con_socket) {
6857 1 uint protocol = MYSQL_PROTOCOL_SOCKET;
6858
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 mysql_options(&con_slot->mysql, MYSQL_OPT_PROTOCOL, &protocol);
6859 }
6860
6861
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 47099 times.
47120 if (con_tcp) {
6862 21 uint protocol = MYSQL_PROTOCOL_TCP;
6863
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 mysql_options(&con_slot->mysql, MYSQL_OPT_PROTOCOL, &protocol);
6864 }
6865
6866 /* Use default db name */
6867
3/4
✓ Branch 0 taken 12492 times.
✓ Branch 1 taken 34628 times.
✓ Branch 2 taken 12492 times.
✗ Branch 3 not taken.
47120 if (ds_database.length == 0) dynstr_set(&ds_database, opt_db);
6868
6869
2/4
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 47120 times.
✗ Branch 3 not taken.
47120 if (opt_plugin_dir && *opt_plugin_dir)
6870
1/2
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
47120 mysql_options(&con_slot->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
6871
6872
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 47093 times.
47120 if (ds_default_auth.length)
6873
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 mysql_options(&con_slot->mysql, MYSQL_DEFAULT_AUTH, ds_default_auth.str);
6874
6875 /* Set server public_key */
6876
1/2
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
47120 set_server_public_key(&con_slot->mysql);
6877
6878
1/2
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
47120 set_get_server_public_key_option(&con_slot->mysql);
6879
6880
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 47113 times.
47120 if (con_cleartext_enable)
6881
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 mysql_options(&con_slot->mysql, MYSQL_ENABLE_CLEARTEXT_PLUGIN,
6882 (char *)&con_cleartext_enable);
6883
6884 47120 unsigned int factor = 0;
6885
2/2
✓ Branch 0 taken 2538 times.
✓ Branch 1 taken 44582 times.
47120 if (ds_password1.length) {
6886 2538 factor = 1;
6887 2538 mysql_options4(&con_slot->mysql, MYSQL_OPT_USER_PASSWORD, &factor,
6888
1/2
✓ Branch 0 taken 2538 times.
✗ Branch 1 not taken.
2538 ds_password1.str);
6889 }
6890 /* set second and third password */
6891
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47120 times.
47120 if (ds_password2.length) {
6892 factor = 2;
6893 mysql_options4(&con_slot->mysql, MYSQL_OPT_USER_PASSWORD, &factor,
6894 ds_password2.str);
6895 }
6896
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47120 times.
47120 if (ds_password3.length) {
6897 factor = 3;
6898 mysql_options4(&con_slot->mysql, MYSQL_OPT_USER_PASSWORD, &factor,
6899 ds_password3.str);
6900 }
6901
6902 /* Special database to allow one to connect without a database name */
6903
3/4
✓ Branch 0 taken 47120 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 59 times.
✓ Branch 3 taken 47061 times.
47120 if (ds_database.length && !std::strcmp(ds_database.str, "*NO-ONE*"))
6904
1/2
✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
59 dynstr_set(&ds_database, "");
6905
6906
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47120 times.
47120 if (getenv("EXTERN")) {
6907 dynstr_set(&ds_host, opt_host);
6908 }
6909
6910
2/2
✓ Branch 0 taken 46434 times.
✓ Branch 1 taken 685 times.
47119 if (connect_n_handle_errors(command, &con_slot->mysql, ds_host.str,
6911 47120 ds_user.str, ds_password1.str, ds_database.str,
6912
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47120 con_port, ds_sock.str)) {
6913
3/8
✓ Branch 0 taken 46434 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46434 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 46434 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
46434 DBUG_PRINT("info", ("Inserting connection %s in connection pool",
6914 ds_connection_name.str));
6915
1/2
✓ Branch 0 taken 46434 times.
✗ Branch 1 not taken.
46434 my_free(con_slot->name);
6916
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46434 times.
46434 if (!(con_slot->name = my_strdup(PSI_NOT_INSTRUMENTED,
6917
1/2
✓ Branch 0 taken 46434 times.
✗ Branch 1 not taken.
46434 ds_connection_name.str, MYF(MY_WME))))
6918 die("Out of memory");
6919 46434 con_slot->name_len = std::strlen(con_slot->name);
6920
1/2
✓ Branch 0 taken 46434 times.
✗ Branch 1 not taken.
46434 set_current_connection(con_slot);
6921
6922
2/2
✓ Branch 0 taken 43456 times.
✓ Branch 1 taken 2978 times.
46434 if (con_slot == next_con)
6923 43456 next_con++; /* if we used the next_con slot, advance the pointer */
6924 }
6925
6926
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_connection_name);
6927
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_host);
6928
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_user);
6929
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_password1);
6930
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_database);
6931
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_port);
6932
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_sock);
6933
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_options);
6934
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_default_auth);
6935
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_shm);
6936
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_compression_algorithm);
6937
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_zstd_compression_level);
6938
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_password2);
6939
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47119 dynstr_free(&ds_password3);
6940 47119 }
6941
6942 281870912 static int do_done(struct st_command *command) {
6943 /* Check if empty block stack */
6944
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 281870909 times.
281870912 if (cur_block == block_stack) {
6945
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (*command->query != '}')
6946 2 die("Stray 'end' command - end of block before beginning");
6947 1 die("Stray '}' - end of block before beginning");
6948 }
6949
6950 /* Test if inner block has been executed */
6951
4/4
✓ Branch 0 taken 13542991 times.
✓ Branch 1 taken 268327918 times.
✓ Branch 2 taken 8584929 times.
✓ Branch 3 taken 4958062 times.
281870909 if (cur_block->ok && cur_block->cmd == cmd_while) {
6952 /* Pop block from stack, re-execute outer block */
6953 8584929 cur_block--;
6954 8584929 parser.current_line = cur_block->line;
6955 } else {
6956
2/2
✓ Branch 0 taken 19415654 times.
✓ Branch 1 taken 253870326 times.
273285980 if (*cur_block->delim) {
6957 /* Restore "old" delimiter after false if block */
6958 19415654 strcpy(delimiter, cur_block->delim);
6959 19415654 delimiter_length = std::strlen(delimiter);
6960 }
6961 /* Pop block from stack, goto next line */
6962 273285980 cur_block--;
6963 273285980 parser.current_line++;
6964 }
6965 281870909 return 0;
6966 }
6967
6968 /* Operands available in if or while conditions */
6969
6970 enum block_op { EQ_OP, NE_OP, GT_OP, GE_OP, LT_OP, LE_OP, ILLEG_OP };
6971
6972 12158501 static enum block_op find_operand(const char *start) {
6973 12158501 char first = *start;
6974 12158501 char next = *(start + 1);
6975
6976
3/4
✓ Branch 0 taken 6325502 times.
✓ Branch 1 taken 5832999 times.
✓ Branch 2 taken 6325502 times.
✗ Branch 3 not taken.
12158501 if (first == '=' && next == '=') return EQ_OP;
6977
3/4
✓ Branch 0 taken 807702 times.
✓ Branch 1 taken 5025297 times.
✓ Branch 2 taken 807702 times.
✗ Branch 3 not taken.
5832999 if (first == '!' && next == '=') return NE_OP;
6978
4/4
✓ Branch 0 taken 220892 times.
✓ Branch 1 taken 4804405 times.
✓ Branch 2 taken 9941 times.
✓ Branch 3 taken 210951 times.
5025297 if (first == '>' && next == '=') return GE_OP;
6979
2/2
✓ Branch 0 taken 210951 times.
✓ Branch 1 taken 4804405 times.
5015356 if (first == '>') return GT_OP;
6980
4/4
✓ Branch 0 taken 4804404 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3723553 times.
✓ Branch 3 taken 1080851 times.
4804405 if (first == '<' && next == '=') return LE_OP;
6981
2/2
✓ Branch 0 taken 1080851 times.
✓ Branch 1 taken 1 times.
1080852 if (first == '<') return LT_OP;
6982
6983 1 return ILLEG_OP;
6984 }
6985
6986 /*
6987 Process start of a "if" or "while" statement
6988
6989 SYNOPSIS
6990 do_block()
6991 cmd Type of block
6992 q called command
6993
6994 DESCRIPTION
6995 if ([!]<expr>)
6996 {
6997 <block statements>
6998 }
6999
7000 while ([!]<expr>)
7001 {
7002 <block statements>
7003 }
7004
7005 assert (expr)
7006
7007 Evaluates the <expr> and if it evaluates to
7008 greater than zero executes the following code block.
7009 A '!' can be used before the <expr> to indicate it should
7010 be executed if it evaluates to zero.
7011
7012 In the assert() case, the block is an implied { die [query]; }
7013
7014 <expr> can also be a simple comparison condition:
7015
7016 <variable> <op> <expr>
7017
7018 The left hand side must be a variable, the right hand side can be a
7019 variable, number, string or `query`. Operands are ==, !=, <, <=, >, >=.
7020 == and != can be used for strings, all can be used for numerical values.
7021 */
7022
7023 281886253 static void do_block(enum block_cmd cmd, struct st_command *command) {
7024 281886253 const char *p = command->first_argument;
7025 const char *expr_start;
7026 const char *expr_end;
7027 VAR v;
7028 const char *cmd_name;
7029
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 281886211 times.
281886253 if (cmd == cmd_assert)
7030 42 cmd_name = "assert";
7031 else
7032
2/2
✓ Branch 0 taken 17924620 times.
✓ Branch 1 taken 263961591 times.
281886211 cmd_name = (cmd == cmd_while ? "while" : "if");
7033 281886253 bool not_expr = false;
7034
1/2
✓ Branch 0 taken 281886253 times.
✗ Branch 1 not taken.
281886253 DBUG_TRACE;
7035
3/8
✓ Branch 0 taken 281886253 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 281886253 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 281886253 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
281886253 DBUG_PRINT("enter", ("%s", cmd_name));
7036
7037 /* Check stack overflow */
7038
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 281886252 times.
281886253 if (cur_block == block_stack_end) die("Nesting too deeply");
7039
7040 /* Set way to find outer block again, increase line counter */
7041 281886252 cur_block->line = parser.current_line++;
7042
7043 /* If this block is ignored */
7044
2/2
✓ Branch 0 taken 248912272 times.
✓ Branch 1 taken 32973980 times.
281886252 if (!cur_block->ok) {
7045
4/4
✓ Branch 0 taken 9004813 times.
✓ Branch 1 taken 239907459 times.
✓ Branch 2 taken 9004805 times.
✓ Branch 3 taken 8 times.
248912272 if (cmd == cmd_if || cmd == cmd_while) {
7046 /* Inner block which comes with the command should be ignored */
7047 248912264 cur_block++;
7048 248912264 cur_block->cmd = cmd;
7049 248912264 cur_block->ok = false;
7050 248912264 cur_block->delim[0] = '\0';
7051 }
7052 /* No need to evaluate the condition */
7053 248912272 return;
7054 }
7055
7056 /* Parse and evaluate test expression */
7057 32973980 expr_start = strchr(p, '(');
7058
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 32973978 times.
32973980 if (!expr_start) die("missing '(' in %s", cmd_name);
7059 32973978 ++expr_start;
7060
7061
2/2
✓ Branch 0 taken 14732 times.
✓ Branch 1 taken 32973978 times.
32988710 while (my_isspace(charset_info, *expr_start)) expr_start++;
7062
7063 /* Check for !<expr> */
7064
2/2
✓ Branch 0 taken 8167586 times.
✓ Branch 1 taken 24806392 times.
32973978 if (*expr_start == '!') {
7065 8167586 not_expr = true;
7066 8167586 expr_start++; /* Step past the '!', then any whitespace */
7067
3/4
✓ Branch 0 taken 8167606 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 8167586 times.
8167606 while (*expr_start && my_isspace(charset_info, *expr_start)) expr_start++;
7068 }
7069 /* Find ending ')' */
7070 32973978 expr_end = strrchr(expr_start, ')');
7071
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 32973977 times.
32973978 if (!expr_end) die("missing ')' in %s", cmd_name);
7072 32973977 p = expr_end + 1;
7073
7074
4/4
✓ Branch 0 taken 60983592 times.
✓ Branch 1 taken 378 times.
✓ Branch 2 taken 28009993 times.
✓ Branch 3 taken 32973599 times.
60983970 while (*p && my_isspace(charset_info, *p)) p++;
7075
2/2
✓ Branch 0 taken 32973599 times.
✓ Branch 1 taken 378 times.
32973977 if (*p) {
7076
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32973599 times.
32973599 if (cmd == cmd_assert)
7077 die("End of line junk detected: \"%s\"", p);
7078
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 32973597 times.
32973599 else if (*p != '{')
7079 2 die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
7080 }
7081
7082
1/2
✓ Branch 0 taken 32973975 times.
✗ Branch 1 not taken.
32973975 var_init(&v, nullptr, 0, nullptr, 0);
7083
7084 /* If expression starts with a variable, it may be a compare condition */
7085
7086
2/2
✓ Branch 0 taken 32711721 times.
✓ Branch 1 taken 262254 times.
32973975 if (*expr_start == '$') {
7087 32711721 const char *curr_ptr = expr_end;
7088
1/2
✓ Branch 0 taken 32711721 times.
✗ Branch 1 not taken.
32711721 eval_expr(&v, expr_start, &curr_ptr, true);
7089
2/2
✓ Branch 0 taken 11530566 times.
✓ Branch 1 taken 32711721 times.
44242287 while (my_isspace(charset_info, *++curr_ptr)) {
7090 }
7091 /* If there was nothing past the variable, skip condition part */
7092
2/2
✓ Branch 0 taken 20553220 times.
✓ Branch 1 taken 12158501 times.
32711721 if (curr_ptr == expr_end) goto NO_COMPARE;
7093
7094 12158501 enum block_op operand = find_operand(curr_ptr);
7095
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 12158500 times.
12158501 if (operand == ILLEG_OP)
7096 1 die("Found junk '%.*s' after $variable in condition",
7097 1 (int)(expr_end - curr_ptr), curr_ptr);
7098
7099 /* We could silently allow this, but may be confusing */
7100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12158500 times.
12158500 if (not_expr)
7101 die("Negation and comparison should not be combined, please rewrite");
7102
7103 /* Skip the 1 or 2 chars of the operand, then white space */
7104
4/4
✓ Branch 0 taken 11077649 times.
✓ Branch 1 taken 1080851 times.
✓ Branch 2 taken 210951 times.
✓ Branch 3 taken 10866698 times.
12158500 if (operand == LT_OP || operand == GT_OP) {
7105 1291802 curr_ptr++;
7106 } else {
7107 10866698 curr_ptr += 2;
7108 }
7109
2/2
✓ Branch 0 taken 11530042 times.
✓ Branch 1 taken 12158500 times.
23688542 while (my_isspace(charset_info, *curr_ptr)) curr_ptr++;
7110
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12158498 times.
12158500 if (curr_ptr == expr_end) die("Missing right operand in comparison");
7111
7112 /* Strip off trailing white space */
7113
2/2
✓ Branch 0 taken 4997 times.
✓ Branch 1 taken 12158498 times.
12163495 while (my_isspace(charset_info, expr_end[-1])) expr_end--;
7114 /* strip off ' or " around the string */
7115
4/4
✓ Branch 0 taken 11257769 times.
✓ Branch 1 taken 900729 times.
✓ Branch 2 taken 65782 times.
✓ Branch 3 taken 11191987 times.
12158498 if (*curr_ptr == '\'' || *curr_ptr == '"') {
7116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 966511 times.
966511 if (expr_end[-1] != *curr_ptr) die("Unterminated string value");
7117 966511 curr_ptr++;
7118 966511 expr_end--;
7119 }
7120 VAR v2;
7121
1/2
✓ Branch 0 taken 12158498 times.
✗ Branch 1 not taken.
12158498 var_init(&v2, nullptr, 0, nullptr, 0);
7122
1/2
✓ Branch 0 taken 12158498 times.
✗ Branch 1 not taken.
12158498 eval_expr(&v2, curr_ptr, &expr_end);
7123
7124
7/8
✓ Branch 0 taken 5832997 times.
✓ Branch 1 taken 6325501 times.
✓ Branch 2 taken 5025295 times.
✓ Branch 3 taken 807702 times.
✓ Branch 4 taken 5025295 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 5025294 times.
12158498 if ((operand != EQ_OP && operand != NE_OP) && !(v.is_int && v2.is_int))
7125 1 die("Only == and != are supported for string values");
7126
7127 /* Now we overwrite the first variable with 0 or 1 (for false or true) */
7128
7129
6/8
✓ Branch 0 taken 6325501 times.
✓ Branch 1 taken 807702 times.
✓ Branch 2 taken 1080851 times.
✓ Branch 3 taken 3723553 times.
✓ Branch 4 taken 210950 times.
✓ Branch 5 taken 9940 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
12158497 switch (operand) {
7130 6325501 case EQ_OP:
7131
2/2
✓ Branch 0 taken 2159923 times.
✓ Branch 1 taken 4165578 times.
6325501 if (v.is_int)
7132
4/4
✓ Branch 0 taken 2101010 times.
✓ Branch 1 taken 58913 times.
✓ Branch 2 taken 132472 times.
✓ Branch 3 taken 1968538 times.
2159923 v.int_val = (v2.is_int && v2.int_val == v.int_val);
7133 else
7134 4165578 v.int_val = !std::strcmp(v.str_val, v2.str_val);
7135 6325501 break;
7136
7137 807702 case NE_OP:
7138
2/2
✓ Branch 0 taken 613194 times.
✓ Branch 1 taken 194508 times.
807702 if (v.is_int)
7139
4/4
✓ Branch 0 taken 608652 times.
✓ Branch 1 taken 4542 times.
✓ Branch 2 taken 53764 times.
✓ Branch 3 taken 554888 times.
613194 v.int_val = !(v2.is_int && v2.int_val == v.int_val);
7140 else
7141 194508 v.int_val = (std::strcmp(v.str_val, v2.str_val) != 0);
7142 807702 break;
7143
7144 1080851 case LT_OP:
7145 1080851 v.int_val = (v.int_val < v2.int_val);
7146 1080851 break;
7147 3723553 case LE_OP:
7148 3723553 v.int_val = (v.int_val <= v2.int_val);
7149 3723553 break;
7150 210950 case GT_OP:
7151 210950 v.int_val = (v.int_val > v2.int_val);
7152 210950 break;
7153 9940 case GE_OP:
7154 9940 v.int_val = (v.int_val >= v2.int_val);
7155 9940 break;
7156 case ILLEG_OP:
7157 die("Impossible operator, this cannot happen");
7158 }
7159
7160 12158497 v.is_int = true;
7161
1/2
✓ Branch 0 taken 12158497 times.
✗ Branch 1 not taken.
12158497 my_free(v2.str_val);
7162 } else {
7163
4/4
✓ Branch 0 taken 1063 times.
✓ Branch 1 taken 261191 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1061 times.
262254 if (*expr_start != '`' && !my_isdigit(charset_info, *expr_start))
7164 2 die("Expression in %s() must begin with $, ` or a number", cmd_name);
7165
1/2
✓ Branch 0 taken 262252 times.
✗ Branch 1 not taken.
262252 eval_expr(&v, expr_start, &expr_end);
7166 }
7167
7168 32973969 NO_COMPARE:
7169 /* Evaluate the expression */
7170 bool v_val_bool;
7171
2/2
✓ Branch 0 taken 26812481 times.
✓ Branch 1 taken 6161488 times.
32973969 if (v.is_int) {
7172 26812481 v_val_bool = (v.int_val != 0);
7173 } else
7174 /* Any non-empty string which does not begin with 0 is also true */
7175 {
7176 6161488 p = v.str_val;
7177 /* First skip any leading white space or unary -+ */
7178
8/8
✓ Branch 0 taken 1046843 times.
✓ Branch 1 taken 5115348 times.
✓ Branch 2 taken 151 times.
✓ Branch 3 taken 1046692 times.
✓ Branch 4 taken 550 times.
✓ Branch 5 taken 1046142 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 1046140 times.
6162191 while (*p && ((my_isspace(charset_info, *p) || *p == '-' || *p == '+')))
7179 703 p++;
7180
4/4
✓ Branch 0 taken 1046140 times.
✓ Branch 1 taken 5115348 times.
✓ Branch 2 taken 1033328 times.
✓ Branch 3 taken 12812 times.
6161488 v_val_bool = (*p && *p != '0');
7181 }
7182
2/2
✓ Branch 0 taken 8167586 times.
✓ Branch 1 taken 24806383 times.
32973969 if (not_expr) v_val_bool = !v_val_bool;
7183
7184 /* Finally, handle assert/if/while */
7185
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 32973936 times.
32973969 if (cmd == cmd_assert) {
7186
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 26 times.
33 if (!v_val_bool) {
7187 7 die("Assertion failed: %s", command->query);
7188 }
7189 } else {
7190 /* Define inner block */
7191 32973936 cur_block++;
7192 32973936 cur_block->cmd = cmd;
7193 32973936 cur_block->ok = v_val_bool;
7194
7195
2/2
✓ Branch 0 taken 13558280 times.
✓ Branch 1 taken 19415656 times.
32973936 if (v_val_bool) {
7196 13558280 cur_block->delim[0] = '\0';
7197 } else {
7198 /* Remember "old" delimiter if entering a false if block */
7199 19415656 strcpy(cur_block->delim, delimiter);
7200 }
7201 }
7202
7203
3/8
✓ Branch 0 taken 32973962 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 32973962 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 32973962 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
32973962 DBUG_PRINT("info", ("OK: %d", v_val_bool));
7204
1/2
✓ Branch 0 taken 32973962 times.
✗ Branch 1 not taken.
32973962 my_free(v.str_val);
7205
2/2
✓ Branch 0 taken 32973962 times.
✓ Branch 1 taken 248912272 times.
281886234 }
7206
7207 106098 static void do_delimiter(struct st_command *command) {
7208 106098 char *p = command->first_argument;
7209
1/2
✓ Branch 0 taken 106098 times.
✗ Branch 1 not taken.
106098 DBUG_TRACE;
7210
3/8
✓ Branch 0 taken 106098 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 106098 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 106098 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
106098 DBUG_PRINT("enter", ("first_argument: %s", command->first_argument));
7211
7212
2/4
✓ Branch 0 taken 106098 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 106098 times.
106098 while (*p && my_isspace(charset_info, *p)) p++;
7213
7214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106098 times.
106098 if (!(*p)) die("Can't set empty delimiter");
7215
7216
1/2
✓ Branch 0 taken 106098 times.
✗ Branch 1 not taken.
106098 strmake(delimiter, p, sizeof(delimiter) - 1);
7217 106098 delimiter_length = std::strlen(delimiter);
7218
7219
3/8
✓ Branch 0 taken 106098 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 106098 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 106098 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
106098 DBUG_PRINT("exit", ("delimiter: %s", delimiter));
7220 106098 command->last_argument = p + delimiter_length;
7221 106098 }
7222
7223 /*
7224 do_reset_connection
7225
7226 DESCRIPTION
7227 Reset the current session.
7228 */
7229 29 static void do_reset_connection() {
7230 29 MYSQL *mysql = &cur_con->mysql;
7231
7232
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
29 DBUG_TRACE;
7233
3/4
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 28 times.
29 if (mysql_reset_connection(mysql))
7234
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 die("reset connection failed: %s", mysql_error(mysql));
7235
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (cur_con->stmt) {
7236 mysql_stmt_close(cur_con->stmt);
7237 cur_con->stmt = nullptr;
7238 }
7239 28 }
7240
7241 2217301789 bool match_delimiter(int c, const char *delim, size_t length) {
7242 uint i;
7243 char tmp[MAX_DELIMITER_LENGTH];
7244
7245
2/2
✓ Branch 0 taken 2198468203 times.
✓ Branch 1 taken 18833586 times.
2217301789 if (c != *delim) return false;
7246
7247
7/8
✓ Branch 0 taken 5591328 times.
✓ Branch 1 taken 14287311 times.
✓ Branch 2 taken 5591328 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1045053 times.
✓ Branch 5 taken 4546275 times.
✓ Branch 6 taken 1045053 times.
✓ Branch 7 taken 18833586 times.
19878639 for (i = 1; i < length && (c = my_getc(cur_file->file)) == *(delim + i); i++)
7248 1045053 tmp[i] = c;
7249
7250
2/2
✓ Branch 0 taken 14287311 times.
✓ Branch 1 taken 4546275 times.
18833586 if (i == length) return true; /* Found delimiter */
7251
7252 /* didn't find delimiter, push back things that we read */
7253 4546275 my_ungetc(c);
7254
2/2
✓ Branch 0 taken 27489 times.
✓ Branch 1 taken 4546275 times.
4573764 while (i > 1) my_ungetc(tmp[--i]);
7255 4546275 return false;
7256 }
7257
7258 1999915019 static bool end_of_query(int c) {
7259 1999915019 return match_delimiter(c, delimiter, delimiter_length);
7260 }
7261
7262 /*
7263 Read one "line" from the file
7264
7265 SYNOPSIS
7266 read_line
7267 buf buffer for the read line
7268 size size of the buffer i.e max size to read
7269
7270 DESCRIPTION
7271 This function actually reads several lines and adds them to the
7272 buffer buf. It continues to read until it finds what it believes
7273 is a complete query.
7274
7275 Normally that means it will read lines until it reaches the
7276 "delimiter" that marks end of query. Default delimiter is ';'
7277 The function should be smart enough not to detect delimiter's
7278 found inside strings surrounded with '"' and '\'' escaped strings.
7279
7280 If the first line in a query starts with '#' or '-' this line is treated
7281 as a comment. A comment is always terminated when end of line '\n' is
7282 reached.
7283
7284 */
7285
7286 441208644 static int read_line(char *buf, int size) {
7287 441208644 char c, last_quote = 0, last_char = 0;
7288 441208644 char *p = buf, *buf_end = buf + size - 1;
7289 441208644 int skip_char = 0;
7290 441208644 int query_comment = 0, query_comment_start = 0, query_comment_end = 0;
7291 441208644 bool have_slash = false;
7292
7293 enum {
7294 R_NORMAL,
7295 R_Q,
7296 R_SLASH_IN_Q,
7297 R_COMMENT,
7298 R_LINE_START
7299 441208644 } state = R_LINE_START;
7300
1/2
✓ Branch 0 taken 441208644 times.
✗ Branch 1 not taken.
441208644 DBUG_TRACE;
7301
7302 441208644 start_lineno = cur_file->lineno;
7303
3/8
✓ Branch 0 taken 441208644 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 441208644 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 441208644 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
441208644 DBUG_PRINT("info", ("Starting to read at lineno: %d", start_lineno));
7304
1/2
✓ Branch 0 taken 16156479419 times.
✗ Branch 1 not taken.
16156479419 for (; p < buf_end;) {
7305 16156479419 skip_char = 0;
7306
1/2
✓ Branch 0 taken 16156479419 times.
✗ Branch 1 not taken.
16156479419 c = my_getc(cur_file->file);
7307
2/2
✓ Branch 0 taken 4480484 times.
✓ Branch 1 taken 16151998935 times.
16156479419 if (feof(cur_file->file)) {
7308 4480484 found_eof:
7309
2/2
✓ Branch 0 taken 4480468 times.
✓ Branch 1 taken 16 times.
4480484 if (cur_file->file != stdin) {
7310
1/2
✓ Branch 0 taken 4480468 times.
✗ Branch 1 not taken.
4480468 fclose(cur_file->file);
7311 4480468 cur_file->file = nullptr;
7312 }
7313
1/2
✓ Branch 0 taken 4480484 times.
✗ Branch 1 not taken.
4480484 my_free(cur_file->file_name);
7314 4480484 cur_file->file_name = nullptr;
7315
2/2
✓ Branch 0 taken 40052 times.
✓ Branch 1 taken 4440432 times.
4480484 if (cur_file == file_stack) {
7316 /* We're back at the first file, check if
7317 all { have matching }
7318 */
7319
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 40050 times.
40052 if (cur_block != block_stack) die("Missing end of block");
7320
7321 40050 *p = 0;
7322
3/8
✓ Branch 0 taken 40050 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 40050 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 40050 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
40050 DBUG_PRINT("info", ("end of file at line %d", cur_file->lineno));
7323 40050 return 1;
7324 }
7325 4440432 cur_file--;
7326 4440432 start_lineno = cur_file->lineno;
7327 4440432 continue;
7328 }
7329
7330
2/2
✓ Branch 0 taken 490297031 times.
✓ Branch 1 taken 15661701904 times.
16151998935 if (c == '\n') {
7331 /* Line counting is independent of state */
7332 490297031 cur_file->lineno++;
7333
7334 /* Convert cr/lf to lf */
7335
4/4
✓ Branch 0 taken 344707907 times.
✓ Branch 1 taken 145589124 times.
✓ Branch 2 taken 975 times.
✓ Branch 3 taken 344706932 times.
490297031 if (p != buf && *(p - 1) == '\r') p--;
7336 }
7337
7338
5/6
✓ Branch 0 taken 1900746657 times.
✓ Branch 1 taken 13110896959 times.
✓ Branch 2 taken 1000827563 times.
✓ Branch 3 taken 139493171 times.
✓ Branch 4 taken 34585 times.
✗ Branch 5 not taken.
16151998935 switch (state) {
7339 1900746657 case R_NORMAL:
7340
3/4
✓ Branch 0 taken 1900746657 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13805703 times.
✓ Branch 3 taken 1886940954 times.
1900746657 if (end_of_query(c)) {
7341 13805703 *p = 0;
7342
3/8
✓ Branch 0 taken 13805703 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13805703 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 13805703 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
13805703 DBUG_PRINT("exit", ("Found delimiter '%s' at line %d", delimiter,
7343 cur_file->lineno));
7344 13805703 return 0;
7345
2/2
✓ Branch 0 taken 42855841 times.
✓ Branch 1 taken 1844085113 times.
1929796795 } else if ((c == '{' &&
7346
2/2
✓ Branch 0 taken 41503883 times.
✓ Branch 1 taken 1351958 times.
42855841 (!charset_info->coll->strnncoll(
7347 charset_info, (const uchar *)"while", 5, (uchar *)buf,
7348
1/2
✓ Branch 0 taken 42855841 times.
✗ Branch 1 not taken.
42855841 std::min<ptrdiff_t>(5, p - buf), false) ||
7349
2/2
✓ Branch 0 taken 41336778 times.
✓ Branch 1 taken 167105 times.
41503883 !charset_info->coll->strnncoll(
7350 charset_info, (const uchar *)"if", 2, (uchar *)buf,
7351
3/4
✓ Branch 0 taken 41503883 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42688736 times.
✓ Branch 3 taken 1844252218 times.
1928444837 std::min<ptrdiff_t>(2, p - buf), false)))) {
7352 /* Only if and while commands can be terminated by { */
7353 42688736 *p++ = c;
7354 42688736 *p = 0;
7355
3/8
✓ Branch 0 taken 42688736 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42688736 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 42688736 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
42688736 DBUG_PRINT("exit", ("Found '{' indicating start of block at line %d",
7356 cur_file->lineno));
7357 42688736 return 0;
7358
6/6
✓ Branch 0 taken 1838431383 times.
✓ Branch 1 taken 5820835 times.
✓ Branch 2 taken 1836839290 times.
✓ Branch 3 taken 1592093 times.
✓ Branch 4 taken 932487 times.
✓ Branch 5 taken 1835906803 times.
1844252218 } else if (c == '\'' || c == '"' || c == '`') {
7359
2/2
✓ Branch 0 taken 8345307 times.
✓ Branch 1 taken 108 times.
8345415 if (!have_slash) {
7360 8345307 last_quote = c;
7361 8345307 state = R_Q;
7362 }
7363
2/2
✓ Branch 0 taken 216931 times.
✓ Branch 1 taken 1835689872 times.
1835906803 } else if (c == '/') {
7364
4/4
✓ Branch 0 taken 213655 times.
✓ Branch 1 taken 3276 times.
✓ Branch 2 taken 210727 times.
✓ Branch 3 taken 2928 times.
216931 if ((query_comment_start == 0) && (query_comment == 0))
7365 210727 query_comment_start = 1;
7366
2/2
✓ Branch 0 taken 2917 times.
✓ Branch 1 taken 3287 times.
6204 else if (query_comment_end == 1) {
7367 2917 query_comment = 0;
7368 2917 query_comment_end = 0;
7369 }
7370
2/2
✓ Branch 0 taken 5170169 times.
✓ Branch 1 taken 1830519703 times.
1835689872 } else if (c == '*') {
7371
4/4
✓ Branch 0 taken 2927 times.
✓ Branch 1 taken 5167242 times.
✓ Branch 2 taken 2926 times.
✓ Branch 3 taken 1 times.
5170169 if ((query_comment == 1) && (query_comment_end == 0))
7372 2926 query_comment_end = 1;
7373
2/2
✓ Branch 0 taken 5085 times.
✓ Branch 1 taken 5162158 times.
5167243 else if (query_comment_start == 1)
7374 5085 query_comment = 1;
7375
4/4
✓ Branch 0 taken 1830233145 times.
✓ Branch 1 taken 286558 times.
✓ Branch 2 taken 14976249 times.
✓ Branch 3 taken 1815256896 times.
1830519703 } else if ((c == '+') || (c == '!')) {
7376
4/4
✓ Branch 0 taken 2164 times.
✓ Branch 1 taken 15260643 times.
✓ Branch 2 taken 2163 times.
✓ Branch 3 taken 1 times.
15262807 if ((query_comment_start == 1) && (query_comment == 1)) {
7377 2163 query_comment_start = 0;
7378 2163 query_comment = 0;
7379 }
7380
2/2
✓ Branch 0 taken 204783 times.
✓ Branch 1 taken 1815052113 times.
1815256896 } else if (query_comment_start == 1)
7381 204783 query_comment_start = 0;
7382
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1815052104 times.
1815052113 else if (query_comment_end == 1)
7383 9 query_comment_end = 0;
7384
7385 1844252218 have_slash = (c == '\\');
7386 1844252218 break;
7387
7388 13110896959 case R_COMMENT:
7389
2/2
✓ Branch 0 taken 311973337 times.
✓ Branch 1 taken 12798923622 times.
13110896959 if (c == '\n') {
7390 /* Comments are terminated by newline */
7391 311973337 *p = 0;
7392
3/8
✓ Branch 0 taken 311973337 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 311973337 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 311973337 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
311973337 DBUG_PRINT("exit", ("Found newline in comment at line: %d",
7393 cur_file->lineno));
7394 311973337 return 0;
7395 }
7396 12798923622 break;
7397
7398 1000827563 case R_LINE_START:
7399
4/4
✓ Branch 0 taken 797375625 times.
✓ Branch 1 taken 203451938 times.
✓ Branch 2 taken 108521400 times.
✓ Branch 3 taken 688854225 times.
1000827563 if (c == '#' || c == '-') {
7400 /* A # or - in the first position of the line - this is a comment */
7401 311973338 state = R_COMMENT;
7402
2/2
✓ Branch 0 taken 589685863 times.
✓ Branch 1 taken 99168362 times.
688854225 } else if (my_isspace(charset_info, c)) {
7403
2/2
✓ Branch 0 taken 145589124 times.
✓ Branch 1 taken 444096739 times.
589685863 if (c == '\n') {
7404
2/2
✓ Branch 0 taken 30026896 times.
✓ Branch 1 taken 115562228 times.
145589124 if (last_char == '\n') {
7405 /* Two new lines in a row, return empty line */
7406
3/8
✓ Branch 0 taken 30026896 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30026896 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 30026896 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
30026896 DBUG_PRINT("info", ("Found two new lines in a row"));
7407 30026896 *p++ = c;
7408 30026896 *p = 0;
7409 30026896 return 0;
7410 }
7411
7412 /* Query hasn't started yet */
7413 115562228 start_lineno = cur_file->lineno;
7414
3/8
✓ Branch 0 taken 115562228 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 115562228 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 115562228 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
115562228 DBUG_PRINT("info", ("Query hasn't started yet, start_lineno: %d",
7415 start_lineno));
7416 }
7417
7418 /* Skip all space at beginning of line */
7419 559658967 skip_char = 1;
7420
3/4
✓ Branch 0 taken 99168362 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 99168361 times.
99168362 } else if (end_of_query(c)) {
7421 1 *p = 0;
7422
3/8
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1 DBUG_PRINT("exit", ("Found delimiter '%s' at line: %d", delimiter,
7423 cur_file->lineno));
7424 1 return 0;
7425
2/2
✓ Branch 0 taken 42673919 times.
✓ Branch 1 taken 56494442 times.
99168361 } else if (c == '}') {
7426 /* A "}" need to be by itself in the beginning of a line to terminate
7427 */
7428 42673919 *p++ = c;
7429 42673919 *p = 0;
7430
3/8
✓ Branch 0 taken 42673919 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42673919 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 42673919 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
42673919 DBUG_PRINT("exit", ("Found '}' in begining of a line at line: %d",
7431 cur_file->lineno));
7432 42673919 return 0;
7433
3/6
✓ Branch 0 taken 56494442 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 56494442 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 56494442 times.
56494442 } else if (c == '\'' || c == '"' || c == '`') {
7434 last_quote = c;
7435 state = R_Q;
7436 } else
7437 56494442 state = R_NORMAL;
7438 928126747 break;
7439
7440 139493171 case R_Q:
7441
2/2
✓ Branch 0 taken 8344962 times.
✓ Branch 1 taken 131148209 times.
139493171 if (c == last_quote)
7442 8344962 state = R_NORMAL;
7443
2/2
✓ Branch 0 taken 34585 times.
✓ Branch 1 taken 131113624 times.
131148209 else if (c == '\\')
7444 34585 state = R_SLASH_IN_Q;
7445
2/2
✓ Branch 0 taken 345 times.
✓ Branch 1 taken 131113279 times.
131113624 else if (query_comment)
7446 345 state = R_NORMAL;
7447 139493171 break;
7448
7449 34585 case R_SLASH_IN_Q:
7450 34585 state = R_Q;
7451 34585 break;
7452 }
7453
7454 15710830343 last_char = c;
7455
7456
2/2
✓ Branch 0 taken 15151171376 times.
✓ Branch 1 taken 559658967 times.
15710830343 if (!skip_char) {
7457 /* Could be a multibyte character */
7458 /* This code is based on the code in "sql_load.cc" */
7459 uint charlen;
7460
2/2
✓ Branch 0 taken 15151152152 times.
✓ Branch 1 taken 19224 times.
15151171376 if (my_mbmaxlenlen(charset_info) == 1)
7461
1/2
✓ Branch 0 taken 15151152152 times.
✗ Branch 1 not taken.
15151152152 charlen = my_mbcharlen(charset_info, (unsigned char)c);
7462 else {
7463
3/4
✓ Branch 0 taken 19224 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1856 times.
✓ Branch 3 taken 17368 times.
19224 if (!(charlen = my_mbcharlen(charset_info, (unsigned char)c))) {
7464
1/2
✓ Branch 0 taken 1856 times.
✗ Branch 1 not taken.
1856 int c1 = my_getc(cur_file->file);
7465
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1856 times.
1856 if (c1 == EOF) {
7466 *p++ = c;
7467 goto found_eof;
7468 }
7469
7470 charlen =
7471
1/2
✓ Branch 0 taken 1856 times.
✗ Branch 1 not taken.
1856 my_mbcharlen_2(charset_info, (unsigned char)c, (unsigned char)c1);
7472 1856 my_ungetc(c1);
7473 }
7474 }
7475
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15151171376 times.
15151171376 if (charlen == 0) return 1;
7476 /* We give up if multibyte character is started but not */
7477 /* completed before we pass buf_end */
7478
3/4
✓ Branch 0 taken 161785 times.
✓ Branch 1 taken 15151009591 times.
✓ Branch 2 taken 161785 times.
✗ Branch 3 not taken.
15151171376 if ((charlen > 1) && (p + charlen) <= buf_end) {
7479 161785 char *mb_start = p;
7480
7481 161785 *p++ = c;
7482
7483
2/2
✓ Branch 0 taken 210949 times.
✓ Branch 1 taken 161785 times.
372734 for (uint i = 1; i < charlen; i++) {
7484
1/2
✓ Branch 0 taken 210949 times.
✗ Branch 1 not taken.
210949 c = my_getc(cur_file->file);
7485
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 210949 times.
210949 if (feof(cur_file->file)) goto found_eof;
7486 210949 *p++ = c;
7487 }
7488
3/4
✓ Branch 0 taken 161785 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 254 times.
✓ Branch 3 taken 161531 times.
161785 if (!my_ismbchar(charset_info, mb_start, p)) {
7489 /* It was not a multiline char, push back the characters */
7490 /* We leave first 'c', i.e. pretend it was a normal char */
7491
2/2
✓ Branch 0 taken 303 times.
✓ Branch 1 taken 254 times.
557 while (p - 1 > mb_start) my_ungetc(*--p);
7492 }
7493 161785 } else
7494 15151009591 *p++ = c;
7495 }
7496 }
7497 die("The input buffer is too small for this query.x\n"
7498 "check your query or increase MAX_QUERY and recompile");
7499 return 0;
7500 441208642 }
7501
7502 /// Set string query attributes for the next query
7503 ///
7504 /// @param command Pointer to the st_command structure which holds the
7505 /// arguments and information for the command. Optionally
7506 /// including a timeout else the default of 60 seconds
7507 10 static void do_query_attributes(struct st_command *command) {
7508 10 const char *from = command->first_argument;
7509 char *buff, *start;
7510
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 DBUG_TRACE;
7511
7512
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 global_attrs->clear();
7513
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (!*from) die("Missing argument in %s", command->query);
7514
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 start = buff = (char *)my_malloc(PSI_NOT_INSTRUMENTED, std::strlen(from) + 1,
7515 MYF(MY_WME | MY_FAE));
7516
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 10 times.
21 while (*from) {
7517
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 char *name = get_string(&buff, &from, command);
7518
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (!*from)
7519 die("Wrong (odd) number of arguments to query_attributes in '%s'",
7520 command->query);
7521
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 char *value = get_string(&buff, &from, command);
7522
7523
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
11 if (global_attrs->push_param(name, value))
7524 die("Failed to add an attribute pair in query_attributes");
7525 }
7526
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 my_free(start);
7527 10 command->last_argument = command->end;
7528 10 }
7529
7530 /*
7531 Convert the read query to result format version 1
7532
7533 That is: After newline, all spaces need to be skipped
7534 unless the previous char was a quote
7535
7536 This is due to an old bug that has now been fixed, but the
7537 version 1 output format is preserved by using this function
7538
7539 */
7540
7541 441168592 static void convert_to_format_v1(char *query) {
7542 441168592 int last_c_was_quote = 0;
7543 441168592 char *p = query, *to = query;
7544 441168592 char *end = strend(query);
7545 char last_c;
7546
7547
2/2
✓ Branch 0 taken 13632027539 times.
✓ Branch 1 taken 441168592 times.
14073196131 while (p <= end) {
7548
4/4
✓ Branch 0 taken 62682685 times.
✓ Branch 1 taken 13569344854 times.
✓ Branch 2 taken 62447725 times.
✓ Branch 3 taken 234960 times.
13632027539 if (*p == '\n' && !last_c_was_quote) {
7549 62447725 *to++ = *p++; /* Save the newline */
7550
7551 /* Skip any spaces on next line */
7552
4/4
✓ Branch 0 taken 84588800 times.
✓ Branch 1 taken 30061355 times.
✓ Branch 2 taken 52202430 times.
✓ Branch 3 taken 32386370 times.
114650155 while (*p && my_isspace(charset_info, *p)) p++;
7553
7554 62447725 last_c_was_quote = 0;
7555
6/6
✓ Branch 0 taken 13538968615 times.
✓ Branch 1 taken 30611199 times.
✓ Branch 2 taken 13536538598 times.
✓ Branch 3 taken 2430017 times.
✓ Branch 4 taken 19107131 times.
✓ Branch 5 taken 13517431467 times.
13569579814 } else if (*p == '\'' || *p == '"' || *p == '`') {
7556 52148347 last_c = *p;
7557 52148347 *to++ = *p++;
7558
7559 /* Copy anything until the next quote of same type */
7560
4/4
✓ Branch 0 taken 2017979949 times.
✓ Branch 1 taken 5729079 times.
✓ Branch 2 taken 1971560681 times.
✓ Branch 3 taken 46419268 times.
2023709028 while (*p && *p != last_c) *to++ = *p++;
7561
7562 52148347 *to++ = *p++;
7563
7564 52148347 last_c_was_quote = 1;
7565 } else {
7566 13517431467 *to++ = *p++;
7567 13517431467 last_c_was_quote = 0;
7568 }
7569 }
7570 441168592 }
7571
7572 /*
7573 Check for unexpected "junk" after the end of query
7574 This is normally caused by missing delimiters or when
7575 switching between different delimiters
7576 */
7577
7578 216012104 void check_eol_junk_line(const char *line) {
7579 216012104 const char *p = line;
7580
1/2
✓ Branch 0 taken 216012104 times.
✗ Branch 1 not taken.
216012104 DBUG_TRACE;
7581
3/8
✓ Branch 0 taken 216012104 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 216012104 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 216012104 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
216012104 DBUG_PRINT("enter", ("line: %s", line));
7582
7583 /* Check for extra delimiter */
7584
4/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 216012096 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 6 times.
216012104 if (*p && !std::strncmp(p, delimiter, delimiter_length))
7585 2 die("Extra delimiter \"%s\" found", delimiter);
7586
7587 /* Allow trailing # comment */
7588
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 216012096 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
216012102 if (*p && *p != '#') {
7589
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (*p == '\n') die("Missing delimiter");
7590 6 die("End of line junk detected: \"%s\"", p);
7591 }
7592 216012096 }
7593
7594 214231944 static void check_eol_junk(const char *eol) {
7595 214231944 const char *p = eol;
7596
1/2
✓ Branch 0 taken 214231944 times.
✗ Branch 1 not taken.
214231944 DBUG_TRACE;
7597
3/8
✓ Branch 0 taken 214231944 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 214231944 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 214231944 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
214231944 DBUG_PRINT("enter", ("eol: %s", eol));
7598
7599 /* Skip past all spacing chars and comments */
7600
7/8
✓ Branch 0 taken 10569 times.
✓ Branch 1 taken 214231937 times.
✓ Branch 2 taken 6846 times.
✓ Branch 3 taken 3723 times.
✓ Branch 4 taken 3716 times.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 7 times.
214242506 while (*p && (my_isspace(charset_info, *p) || *p == '#' || *p == '\n')) {
7601 /* Skip past comments started with # and ended with newline */
7602
3/4
✓ Branch 0 taken 10562 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3716 times.
✓ Branch 3 taken 6846 times.
10562 if (*p && *p == '#') {
7603 3716 p++;
7604
3/4
✓ Branch 0 taken 116570 times.
✓ Branch 1 taken 3716 times.
✓ Branch 2 taken 116570 times.
✗ Branch 3 not taken.
120286 while (*p && *p != '\n') p++;
7605 }
7606
7607 /* Check this line */
7608
3/6
✓ Branch 0 taken 6846 times.
✓ Branch 1 taken 3716 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6846 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
10562 if (*p && *p == '\n') check_eol_junk_line(p);
7609
7610
2/2
✓ Branch 0 taken 6846 times.
✓ Branch 1 taken 3716 times.
10562 if (*p) p++;
7611 }
7612
7613
1/2
✓ Branch 0 taken 214231937 times.
✗ Branch 1 not taken.
214231944 check_eol_junk_line(p);
7614 214231937 }
7615
7616 1004874446 static bool is_delimiter(const char *p) {
7617 1004874446 uint match = 0;
7618 1004874446 char *delim = delimiter;
7619
6/6
✓ Branch 0 taken 1004874467 times.
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 99 times.
✓ Branch 3 taken 1004874368 times.
✓ Branch 4 taken 99 times.
✓ Branch 5 taken 1004874446 times.
1004874545 while (*p && *p == *delim++) {
7620 99 match++;
7621 99 p++;
7622 }
7623
7624 1004874446 return (match == delimiter_length);
7625 }
7626
7627 // 256K -- a test in sp-big is >128K
7628 #define MAX_QUERY (256 * 1024 * 2)
7629 static char read_command_buf[MAX_QUERY];
7630
7631 /// Create a command from a set of lines.
7632 ///
7633 /// Converts lines returned by read_line into a command, this involves
7634 /// parsing the first word in the read line to find the command type.
7635 ///
7636 /// A '`--`' comment may contain a valid query as the first word after
7637 /// the comment start. Thus it's always checked to see if that is the
7638 /// case. The advantage with this approach is to be able to execute
7639 /// commands terminated by new line '\n' regardless how many "delimiter"
7640 /// it contain.
7641 ///
7642 /// @param [in] command_ptr pointer where to return the new query
7643 ///
7644 /// @retval 0 on success, else 1
7645 2830857733 static int read_command(struct st_command **command_ptr) {
7646 2830857733 char *p = read_command_buf;
7647
1/2
✓ Branch 0 taken 2830857733 times.
✗ Branch 1 not taken.
2830857733 DBUG_TRACE;
7648
7649
2/2
✓ Branch 0 taken 2389649089 times.
✓ Branch 1 taken 441208644 times.
2830857733 if (parser.current_line < parser.read_lines) {
7650
1/2
✓ Branch 0 taken 2389649089 times.
✗ Branch 1 not taken.
2389649089 *command_ptr = q_lines->at(parser.current_line);
7651 // Assign the current command line number
7652 2389649089 start_lineno = (*command_ptr)->lineno;
7653 2389649089 return 0;
7654 }
7655
7656 struct st_command *command;
7657
1/2
✓ Branch 0 taken 441208644 times.
✗ Branch 1 not taken.
441208644 if (!(*command_ptr = command = (struct st_command *)my_malloc(
7658 PSI_NOT_INSTRUMENTED, sizeof(*command),
7659
2/4
✓ Branch 0 taken 441208644 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 441208644 times.
882417288 MYF(MY_WME | MY_ZEROFILL))) ||
7660
2/4
✓ Branch 0 taken 441208644 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 441208644 times.
441208644 q_lines->push_back(command))
7661 die("Out of memory");
7662 441208644 command->type = Q_UNKNOWN;
7663
7664 441208644 read_command_buf[0] = 0;
7665
3/4
✓ Branch 0 taken 441208642 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 40050 times.
✓ Branch 3 taken 441168592 times.
441208644 if (read_line(read_command_buf, sizeof(read_command_buf))) {
7666
1/2
✓ Branch 0 taken 40047 times.
✗ Branch 1 not taken.
40050 check_eol_junk(read_command_buf);
7667 40047 return 1;
7668 }
7669
7670 // Set the line number for the command
7671 441168592 command->lineno = start_lineno;
7672
7673
1/2
✓ Branch 0 taken 441168592 times.
✗ Branch 1 not taken.
441168592 if (opt_result_format_version == 1) convert_to_format_v1(read_command_buf);
7674
7675
3/8
✓ Branch 0 taken 441168592 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 441168592 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 441168592 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
441168592 DBUG_PRINT("info", ("query: '%s'", read_command_buf));
7676
2/2
✓ Branch 0 taken 203451937 times.
✓ Branch 1 taken 237716655 times.
441168592 if (*p == '#') {
7677 203451937 command->type = Q_COMMENT;
7678
3/4
✓ Branch 0 taken 108521400 times.
✓ Branch 1 taken 129195255 times.
✓ Branch 2 taken 108521400 times.
✗ Branch 3 not taken.
237716655 } else if (p[0] == '-' && p[1] == '-') {
7679 108521400 command->type = Q_COMMENT_WITH_COMMAND;
7680 // Skip past '--'
7681 108521400 p += 2;
7682
2/2
✓ Branch 0 taken 30026896 times.
✓ Branch 1 taken 99168359 times.
129195255 } else if (*p == '\n') {
7683 30026896 command->type = Q_EMPTY_LINE;
7684 }
7685
7686 // Skip leading spaces
7687
4/4
✓ Branch 0 taken 441197614 times.
✓ Branch 1 taken 30026897 times.
✓ Branch 2 taken 30055919 times.
✓ Branch 3 taken 411141695 times.
471224511 while (*p && my_isspace(charset_info, *p)) p++;
7688
7689 882337184 if (!(command->query_buf = command->query =
7690
2/4
✓ Branch 0 taken 441168592 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 441168592 times.
441168592 my_strdup(PSI_NOT_INSTRUMENTED, p, MYF(MY_WME))))
7691 die("Out of memory");
7692
7693 // Calculate first word length(the command), terminated
7694 // by 'space' , '(' or 'delimiter'
7695 441168592 p = command->query;
7696
10/10
✓ Branch 0 taken 1310370806 times.
✓ Branch 1 taken 135672154 times.
✓ Branch 2 taken 1005184014 times.
✓ Branch 3 taken 305186792 times.
✓ Branch 4 taken 1004874446 times.
✓ Branch 5 taken 309568 times.
✓ Branch 6 taken 1004874368 times.
✓ Branch 7 taken 78 times.
✓ Branch 8 taken 1004874368 times.
✓ Branch 9 taken 441168592 times.
1446042960 while (*p && !my_isspace(charset_info, *p) && *p != '(' && !is_delimiter(p))
7697 1004874368 p++;
7698 441168592 command->first_word_len = (uint)(p - command->query);
7699
3/8
✓ Branch 0 taken 441168592 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 441168592 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 441168592 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
441168592 DBUG_PRINT("info",
7700 ("first_word: %.*s", static_cast<int>(command->first_word_len),
7701 command->query));
7702
7703 // Skip spaces between command and first argument
7704
4/4
✓ Branch 0 taken 866381515 times.
✓ Branch 1 taken 135681221 times.
✓ Branch 2 taken 560894144 times.
✓ Branch 3 taken 305487371 times.
1002062736 while (*p && my_isspace(charset_info, *p)) p++;
7705 441168592 command->first_argument = p;
7706
7707 441168592 command->end = strend(command->query);
7708 441168592 command->query_len = (command->end - command->query);
7709 441168592 parser.read_lines++;
7710 441168592 return 0;
7711 2830857728 }
7712
7713 static struct my_option my_long_options[] = {
7714 #include "caching_sha2_passwordopt-longopts.h"
7715 #include "sslopt-longopts.h"
7716 {"basedir", 'b', "Basedir for tests.", &opt_basedir, &opt_basedir, nullptr,
7717 GET_STR, REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
7718 {"character-sets-dir", OPT_CHARSETS_DIR,
7719 "Directory for character set files.", &opt_charsets_dir, &opt_charsets_dir,
7720 nullptr, GET_STR, REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
7721 {"colored-diff", OPT_COLORED_DIFF, "Colorize the diff outout.",
7722 &opt_colored_diff, &opt_colored_diff, nullptr, GET_BOOL, NO_ARG, 0, 0, 0,
7723 nullptr, 0, nullptr},
7724 {"compress", 'C', "Use the compressed server/client protocol.",
7725 &opt_compress, &opt_compress, nullptr, GET_BOOL, NO_ARG, 0, 0, 0, nullptr,
7726 0, nullptr},
7727 {"connect_timeout", OPT_CONNECT_TIMEOUT,
7728 "Number of seconds before connection timeout.", &opt_connect_timeout,
7729 &opt_connect_timeout, nullptr, GET_UINT, REQUIRED_ARG, 120, 0, 3600 * 12,
7730 nullptr, 0, nullptr},
7731 {"cursor-protocol", OPT_CURSOR_PROTOCOL,
7732 "Use cursors for prepared statements.", &cursor_protocol, &cursor_protocol,
7733 nullptr, GET_BOOL, NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
7734 {"database", 'D', "Database to use.", &opt_db, &opt_db, nullptr, GET_STR,
7735 REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
7736 #ifdef NDEBUG
7737 {"debug", '#', "This is a non-debug version. Catch this and exit.", nullptr,
7738 nullptr, nullptr, GET_DISABLED, OPT_ARG, 0, 0, 0, nullptr, 0, nullptr},
7739 {"debug-check", OPT_DEBUG_CHECK,
7740 "This is a non-debug version. Catch this and exit.", nullptr, nullptr,
7741 nullptr, GET_DISABLED, NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
7742 {"debug-info", OPT_DEBUG_INFO,
7743 "This is a non-debug version. Catch this and exit.", nullptr, nullptr,
7744 nullptr, GET_DISABLED, NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
7745 #else
7746 {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", nullptr,
7747 nullptr, nullptr, GET_STR, OPT_ARG, 0, 0, 0, nullptr, 0, nullptr},
7748 {"debug-check", OPT_DEBUG_CHECK,
7749 "Check memory and open file usage at exit.", &debug_check_flag,
7750 &debug_check_flag, nullptr, GET_BOOL, NO_ARG, 0, 0, 0, nullptr, 0,
7751 nullptr},
7752 {"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.",
7753 &debug_info_flag, &debug_info_flag, nullptr, GET_BOOL, NO_ARG, 0, 0, 0,
7754 nullptr, 0, nullptr},
7755 #endif
7756 {"default-character-set", OPT_DEFAULT_CHARSET,
7757 "Set the default character set.", &default_charset, &default_charset,
7758 nullptr, GET_STR, REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
7759 {"explain-protocol", OPT_EXPLAIN_PROTOCOL,
7760 "Explain all SELECT/INSERT/REPLACE/UPDATE/DELETE statements",
7761 &explain_protocol, &explain_protocol, nullptr, GET_BOOL, NO_ARG, 0, 0, 0,
7762 nullptr, 0, nullptr},
7763 {"help", '?', "Display this help and exit.", nullptr, nullptr, nullptr,
7764 GET_NO_ARG, NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
7765 {"host", 'h', "Connect to host.", &opt_host, &opt_host, nullptr, GET_STR,
7766 REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
7767 {"hypergraph", OPT_HYPERGRAPH,
7768 "Force all queries to be run under the hypergraph optimizer.",
7769 &opt_hypergraph, &opt_hypergraph, nullptr, GET_BOOL, NO_ARG, 0, 0, 0,
7770 nullptr, 0, nullptr},
7771 {"include", 'i', "Include SQL before each test case.", &opt_include,
7772 &opt_include, nullptr, GET_STR, REQUIRED_ARG, 0, 0, 0, nullptr, 0,
7773 nullptr},
7774 {"json-explain-protocol", OPT_JSON_EXPLAIN_PROTOCOL,
7775 "Explain all SELECT/INSERT/REPLACE/UPDATE/DELETE statements with "
7776 "FORMAT=JSON",
7777 &json_explain_protocol, &json_explain_protocol, nullptr, GET_BOOL, NO_ARG,
7778 0, 0, 0, nullptr, 0, nullptr},
7779 {"logdir", OPT_LOG_DIR, "Directory for log files", &opt_logdir, &opt_logdir,
7780 nullptr, GET_STR, REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
7781 {"mark-progress", OPT_MARK_PROGRESS,
7782 "Write line number and elapsed time to <testname>.progress.",
7783 &opt_mark_progress, &opt_mark_progress, nullptr, GET_BOOL, NO_ARG, 0, 0, 0,
7784 nullptr, 0, nullptr},
7785 {"init-command", OPT_INIT_COMMAND,
7786 "SQL Command to execute when connecting to MySQL server. Will "
7787 "automatically be re-executed when reconnecting.",
7788 &opt_init_command, &opt_init_command, nullptr, GET_STR, REQUIRED_ARG, 0, 0,
7789 0, nullptr, 0, nullptr},
7790 {"max-connect-retries", OPT_MAX_CONNECT_RETRIES,
7791 "Maximum number of attempts to connect to server.",
7792 &opt_max_connect_retries, &opt_max_connect_retries, nullptr, GET_INT,
7793 REQUIRED_ARG, 500, 1, 10000, nullptr, 0, nullptr},
7794 {"max-connections", OPT_MAX_CONNECTIONS,
7795 "Max number of open connections to server", &opt_max_connections,
7796 &opt_max_connections, nullptr, GET_INT, REQUIRED_ARG, DEFAULT_MAX_CONN, 8,
7797 9120, nullptr, 0, nullptr},
7798 {"no-skip", OPT_NO_SKIP, "Force the test to run without skip.", &no_skip,
7799 &no_skip, nullptr, GET_BOOL, NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
7800 {"no-skip-exclude-list", 'n',
7801 "Contains comma-separated list of to be excluded inc files.",
7802 &excluded_string, &excluded_string, nullptr, GET_STR, REQUIRED_ARG, 0, 0,
7803 0, nullptr, 0, nullptr},
7804 {"offload-count-file", OPT_OFFLOAD_COUNT_FILE, "Offload count report file",
7805 &opt_offload_count_file, &opt_offload_count_file, nullptr, GET_STR,
7806 REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
7807 {"opt-trace-protocol", OPT_TRACE_PROTOCOL,
7808 "Trace DML statements with optimizer trace", &opt_trace_protocol,
7809 &opt_trace_protocol, nullptr, GET_BOOL, NO_ARG, 0, 0, 0, nullptr, 0,
7810 nullptr},
7811 {"password", 'p', "Password to use when connecting to server.", nullptr,
7812 nullptr, nullptr, GET_STR, OPT_ARG, 0, 0, 0, nullptr, 0, nullptr},
7813 {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
7814 &opt_plugin_dir, &opt_plugin_dir, nullptr, GET_STR, REQUIRED_ARG, 0, 0, 0,
7815 nullptr, 0, nullptr},
7816 {"port", 'P',
7817 "Port number to use for connection or 0 for default to, in "
7818 "order of preference, my.cnf, $MYSQL_TCP_PORT, "
7819 #if MYSQL_PORT_DEFAULT == 0
7820 "/etc/services, "
7821 #endif
7822 "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
7823 &opt_port, &opt_port, nullptr, GET_INT, REQUIRED_ARG, 0, 0, 0, nullptr, 0,
7824 nullptr},
7825 {"protocol", OPT_MYSQL_PROTOCOL,
7826 "The protocol of connection (tcp,socket,pipe,memory).", nullptr, nullptr,
7827 nullptr, GET_STR, REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
7828 {"ps-protocol", OPT_PS_PROTOCOL,
7829 "Use prepared-statement protocol for communication.", &ps_protocol,
7830 &ps_protocol, nullptr, GET_BOOL, NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
7831 {"quiet", 's', "Suppress all normal output.", &silent, &silent, nullptr,
7832 GET_BOOL, NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
7833 {"record", 'r', "Record output of test_file into result file.", nullptr,
7834 nullptr, nullptr, GET_NO_ARG, NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
7835 {"result-file", 'R', "Read/store result from/in this file.",
7836 &result_file_name, &result_file_name, nullptr, GET_STR, REQUIRED_ARG, 0, 0,
7837 0, nullptr, 0, nullptr},
7838 {"result-format-version", OPT_RESULT_FORMAT_VERSION,
7839 "Version of the result file format to use", &opt_result_format_version,
7840 &opt_result_format_version, nullptr, GET_INT, REQUIRED_ARG, 1, 1, 2,
7841 nullptr, 0, nullptr},
7842 #ifdef _WIN32
7843 {"safe-process-pid", OPT_SAFEPROCESS_PID, "PID of safeprocess.",
7844 &opt_safe_process_pid, &opt_safe_process_pid, 0, GET_INT, REQUIRED_ARG, 0,
7845 0, 0, 0, 0, 0},
7846 #endif
7847 {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME,
7848 "Base name of shared memory.", &shared_memory_base_name,
7849 &shared_memory_base_name, nullptr, GET_STR, REQUIRED_ARG, 0, 0, 0, nullptr,
7850 0, nullptr},
7851 {"silent", 's', "Suppress all normal output. Synonym for --quiet.", &silent,
7852 &silent, nullptr, GET_BOOL, NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
7853 {"socket", 'S', "The socket file to use for connection.", &unix_sock,
7854 &unix_sock, nullptr, GET_STR, REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
7855 {"sp-protocol", OPT_SP_PROTOCOL, "Use stored procedures for select.",
7856 &sp_protocol, &sp_protocol, nullptr, GET_BOOL, NO_ARG, 0, 0, 0, nullptr, 0,
7857 nullptr},
7858 {"tail-lines", OPT_TAIL_LINES,
7859 "Number of lines of the result to include in a failure report.",
7860 &opt_tail_lines, &opt_tail_lines, nullptr, GET_INT, REQUIRED_ARG, 0, 0,
7861 10000, nullptr, 0, nullptr},
7862 {"test-file", 'x', "Read test from/in this file (default stdin).", nullptr,
7863 nullptr, nullptr, GET_STR, REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
7864 {"timer-file", 'm', "File where the timing in microseconds is stored.",
7865 nullptr, nullptr, nullptr, GET_STR, REQUIRED_ARG, 0, 0, 0, nullptr, 0,
7866 nullptr},
7867 {"tmpdir", 't', "Temporary directory where sockets are put.", nullptr,
7868 nullptr, nullptr, GET_STR, REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
7869 {"trace-exec", OPT_TRACE_EXEC, "Print output from exec to stdout.",
7870 &trace_exec, &trace_exec, nullptr, GET_BOOL, NO_ARG, 0, 0, 0, nullptr, 0,
7871 nullptr},
7872 {"user", 'u', "User for login.", &opt_user, &opt_user, nullptr, GET_STR,
7873 REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
7874 {"verbose", 'v', "Write more.", &verbose, &verbose, nullptr, GET_BOOL,
7875 NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
7876 {"version", 'V', "Output version information and exit.", nullptr, nullptr,
7877 nullptr, GET_NO_ARG, NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
7878 {"view-protocol", OPT_VIEW_PROTOCOL, "Use views for select.",
7879 &view_protocol, &view_protocol, nullptr, GET_BOOL, NO_ARG, 0, 0, 0,
7880 nullptr, 0, nullptr},
7881 {"async-client", '*', "Use async client.", &use_async_client,
7882 &use_async_client, nullptr, GET_BOOL, NO_ARG, 0, 0, 0, nullptr, 0,
7883 nullptr},
7884 {"compression-algorithms", 0,
7885 "Use compression algorithm in server/client protocol. Valid values "
7886 "are any combination of 'zstd','zlib','uncompressed'.",
7887 &opt_compress_algorithm, &opt_compress_algorithm, nullptr, GET_STR,
7888 REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
7889 {"zstd-compression-level", 0,
7890 "Use this compression level in the client/server protocol, in case "
7891 "--compression-algorithms=zstd. Valid range is between 1 and 22, "
7892 "inclusive. Default is 3.",
7893 &opt_zstd_compress_level, &opt_zstd_compress_level, nullptr, GET_UINT,
7894 REQUIRED_ARG, 3, 1, 22, nullptr, 0, nullptr},
7895 {"test-ssl-fips-mode", 0,
7896 "Toggle SSL FIPS mode on or off, to see whether FIPS is supported. "
7897 "Prints the result to stdout, and then exits. "
7898 "Used by mtr to enable/disable FIPS tests. ",
7899 &opt_test_ssl_fips_mode, nullptr, nullptr, GET_BOOL, NO_ARG, 0, 0, 0,
7900 nullptr, 0, nullptr},
7901
7902 {nullptr, 0, nullptr, nullptr, nullptr, nullptr, GET_NO_ARG, NO_ARG, 0, 0,
7903 0, nullptr, 0, nullptr}};
7904
7905 2 static void usage() {
7906 2 print_version();
7907 2 puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
7908 2 printf(
7909 "Runs a test against the mysql server and compares output with a results "
7910 "file.\n\n");
7911 2 printf("Usage: %s [OPTIONS] [database] < test_file\n", my_progname);
7912 2 my_print_help(my_long_options);
7913 2 printf(
7914 " --no-defaults Don't read default options from any options "
7915 "file.\n");
7916 2 my_print_variables(my_long_options);
7917 2 }
7918
7919 942336 static bool get_one_option(int optid, const struct my_option *opt,
7920 char *argument) {
7921
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 942336 times.
942336 if (opt_test_ssl_fips_mode) {
7922 char ssl_err_string[OPENSSL_ERROR_LENGTH] = {'\0'};
7923 int fips_test = test_ssl_fips_mode(ssl_err_string);
7924 fprintf(stdout, "--test-ssl-fips-mode %d %s\n", fips_test,
7925 fips_test == 0 ? ssl_err_string : "Success");
7926 exit(0);
7927 }
7928
13/16
✗ Branch 0 not taken.
✓ Branch 1 taken 15501 times.
✓ Branch 2 taken 55256 times.
✓ Branch 3 taken 11955 times.
✓ Branch 4 taken 99075 times.
✓ Branch 5 taken 55548 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 22 times.
✓ Branch 8 taken 52 times.
✓ Branch 9 taken 11955 times.
✓ Branch 10 taken 55515 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 1 times.
✓ Branch 15 taken 637453 times.
942336 switch (optid) {
7929 case '#':
7930 #ifndef NDEBUG
7931 DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace");
7932 debug_check_flag = true;
7933 #endif
7934 break;
7935 15501 case 'r':
7936 15501 record = 1;
7937 15501 break;
7938 55256 case 'x': {
7939 char buff[FN_REFLEN];
7940
3/4
✓ Branch 0 taken 55256 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 43561 times.
✓ Branch 3 taken 11695 times.
55256 if (!test_if_hard_path(argument)) {
7941
1/2
✓ Branch 0 taken 43561 times.
✗ Branch 1 not taken.
43561 strxmov(buff, opt_basedir, argument, NullS);
7942 43561 argument = buff;
7943 }
7944
1/2
✓ Branch 0 taken 55256 times.
✗ Branch 1 not taken.
55256 fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
7945
2/4
✓ Branch 0 taken 55256 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 55256 times.
✗ Branch 3 not taken.
55256 assert(cur_file == file_stack && cur_file->file == nullptr);
7946
3/4
✓ Branch 0 taken 55256 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 55255 times.
55256 if (!(cur_file->file = fopen(buff, "rb")))
7947 1 die("Could not open '%s' for reading, errno: %d", buff, errno);
7948
1/2
✓ Branch 0 taken 55255 times.
✗ Branch 1 not taken.
55255 cur_file->file_name = my_strdup(PSI_NOT_INSTRUMENTED, buff, MYF(MY_FAE));
7949 55255 cur_file->lineno = 1;
7950 55255 break;
7951 }
7952 11955 case 'm': {
7953 static char buff[FN_REFLEN];
7954
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11955 times.
11955 if (!test_if_hard_path(argument)) {
7955 strxmov(buff, opt_basedir, argument, NullS);
7956 argument = buff;
7957 }
7958 11955 fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
7959 11955 timer_file = buff;
7960 11955 unlink(timer_file); /* Ignore error, may not exist */
7961 11955 break;
7962 }
7963 99075 case 'p':
7964
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 99075 times.
99075 if (argument == disabled_my_option) {
7965 // Don't require password
7966 static char empty_password[] = {'\0'};
7967 assert(empty_password[0] ==
7968 '\0'); // Check that it has not been overwritten
7969 argument = empty_password;
7970 }
7971
1/2
✓ Branch 0 taken 99075 times.
✗ Branch 1 not taken.
99075 if (argument) {
7972 99075 my_free(opt_pass);
7973 99075 opt_pass = my_strdup(PSI_NOT_INSTRUMENTED, argument, MYF(MY_FAE));
7974
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 99075 times.
99075 while (*argument) *argument++ = 'x'; /* Destroy argument */
7975 99075 tty_password = false;
7976 } else
7977 tty_password = true;
7978 99075 break;
7979 #include "sslopt-case.h"
7980
7981 11955 case 't':
7982 11955 my_stpnmov(TMPDIR, argument, sizeof(TMPDIR));
7983 11955 break;
7984 55515 case OPT_LOG_DIR:
7985 /* Check that the file exists */
7986
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55515 times.
55515 if (access(opt_logdir, F_OK) != 0)
7987 die("The specified log directory does not exist: '%s'", opt_logdir);
7988 55515 break;
7989 case OPT_RESULT_FORMAT_VERSION:
7990 set_result_format_version(opt_result_format_version);
7991 break;
7992 1 case 'V':
7993 1 print_version();
7994 1 exit(0);
7995 2 case OPT_MYSQL_PROTOCOL:
7996 2 opt_protocol =
7997 2 find_type_or_exit(argument, &sql_protocol_typelib, opt->name);
7998 2 break;
7999 1 case '?':
8000 1 usage();
8001 1 exit(0);
8002 }
8003 942333 return false;
8004 }
8005
8006 /**
8007 Test case or the result file names may use alphanumeric characters
8008 (A-Z, a-z, 0-9), dash ('-') or underscore ('_'), but should not
8009 start with dash or underscore.
8010
8011 Check if a file name contains any other special characters. If yes,
8012 throw an error and abort the test run.
8013
8014 @param[in] file_name File name
8015 */
8016
8017 96529 static void validate_filename(const char *file_name) {
8018 96529 const char *fname = strrchr(file_name, '/');
8019
8020
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96529 times.
96529 if (fname == nullptr) {
8021 if (is_windows) {
8022 fname = strrchr(file_name, '\\');
8023
8024 if (fname == nullptr)
8025 fname = file_name;
8026 else
8027 fname++;
8028 } else
8029 fname = file_name;
8030 } else
8031 96529 fname++;
8032
8033 96529 file_name = fname;
8034
8035 // Check if first character in the file name is a alphanumeric character
8036
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 96527 times.
96529 if (!my_isalnum(charset_info, file_name[0])) {
8037 2 die("Invalid file name '%s', first character must be alpha-numeric.",
8038 file_name);
8039 } else
8040 96527 file_name++;
8041
8042 // Skip extension('.test' or '.result' or '.inc' etc) in the file name
8043 96527 const char *file_name_end = strrchr(file_name, '.');
8044
8045
3/4
✓ Branch 0 taken 1555747 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1459223 times.
✓ Branch 3 taken 96524 times.
1555747 while (*file_name && (file_name != file_name_end) &&
8046
4/4
✓ Branch 0 taken 74123 times.
✓ Branch 1 taken 1385100 times.
✓ Branch 2 taken 95016 times.
✓ Branch 3 taken 1290084 times.
1459223 (file_name[0] == '-' || file_name[0] == '_' ||
8047
2/2
✓ Branch 0 taken 1290081 times.
✓ Branch 1 taken 3 times.
1290084 my_isalnum(charset_info, file_name[0]))) {
8048 1459220 file_name++;
8049 }
8050
8051
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 96524 times.
96527 if (file_name != file_name_end) {
8052 3 die("Invalid file name '%s'. Test or result file name should "
8053 "consist of only alpha-numeric characters, dash (-) or "
8054 "underscore (_), but should not start with dash or "
8055 "underscore.",
8056 fname);
8057 }
8058 96524 }
8059
8060 55515 static int parse_args(int argc, char **argv) {
8061
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55515 times.
55515 if (load_defaults("my", load_default_groups, &argc, &argv, &argv_alloc))
8062 exit(1);
8063
8064
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55512 times.
55515 if ((handle_options(&argc, &argv, my_long_options, get_one_option))) exit(1);
8065
8066 // Check for special characters in test case file name
8067
2/2
✓ Branch 0 taken 55255 times.
✓ Branch 1 taken 257 times.
55512 if (cur_file->file_name) validate_filename(cur_file->file_name);
8068
8069 // Check for special characters in result file name
8070
2/2
✓ Branch 0 taken 41274 times.
✓ Branch 1 taken 14234 times.
55508 if (result_file_name) validate_filename(result_file_name);
8071
8072
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 55506 times.
55507 if (argc > 1) {
8073 1 usage();
8074 1 exit(1);
8075 }
8076
8077
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55506 times.
55506 if (argc == 1) opt_db = *argv;
8078
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55506 times.
55506 if (tty_password) opt_pass = get_tty_password(NullS);
8079
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55506 times.
55506 if (debug_info_flag) my_end_arg = MY_CHECK_ERROR | MY_GIVE_INFO;
8080
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55506 times.
55506 if (debug_check_flag) my_end_arg = MY_CHECK_ERROR;
8081
8082
2/2
✓ Branch 0 taken 40005 times.
✓ Branch 1 taken 15501 times.
55506 if (!record) {
8083 /* Check that the result file exists */
8084
4/6
✓ Branch 0 taken 25772 times.
✓ Branch 1 taken 14233 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 25772 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 40005 times.
40005 if (result_file_name && access(result_file_name, F_OK) != 0)
8085 die("The specified result file '%s' does not exist", result_file_name);
8086 }
8087
8088 55506 return 0;
8089 }
8090
8091 /*
8092 Write the content of str into file
8093
8094 SYNOPSIS
8095 str_to_file2
8096 fname - name of file to truncate/create and write to
8097 str - content to write to file
8098 size - size of content witten to file
8099 append - append to file instead of overwriting old file
8100 */
8101
8102 382889 void str_to_file2(const char *fname, char *str, size_t size, bool append) {
8103 int fd;
8104 char buff[FN_REFLEN];
8105 382889 int flags = O_WRONLY | O_CREAT;
8106
2/4
✓ Branch 0 taken 382889 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 382889 times.
382889 if (!test_if_hard_path(fname)) {
8107 strxmov(buff, opt_basedir, fname, NullS);
8108 fname = buff;
8109 }
8110
1/2
✓ Branch 0 taken 382889 times.
✗ Branch 1 not taken.
382889 fn_format(buff, fname, "", "", MY_UNPACK_FILENAME);
8111
8112
2/2
✓ Branch 0 taken 84701 times.
✓ Branch 1 taken 298188 times.
382889 if (!append) flags |= O_TRUNC;
8113
2/4
✓ Branch 0 taken 382889 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 382889 times.
382889 if ((fd = my_open(buff, flags, MYF(MY_WME))) < 0)
8114 die("Could not open '%s' for writing, errno: %d", buff, errno);
8115
5/8
✓ Branch 0 taken 298188 times.
✓ Branch 1 taken 84701 times.
✓ Branch 2 taken 298188 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 298188 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 382889 times.
382889 if (append && my_seek(fd, 0, SEEK_END, MYF(0)) == MY_FILEPOS_ERROR)
8116 die("Could not find end of file '%s', errno: %d", buff, errno);
8117
2/4
✓ Branch 0 taken 382889 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 382889 times.
382889 if (my_write(fd, (uchar *)str, size, MYF(MY_WME | MY_FNABP)))
8118 die("write failed, errno: %d", errno);
8119
1/2
✓ Branch 0 taken 382889 times.
✗ Branch 1 not taken.
382889 my_close(fd, MYF(0));
8120 382889 }
8121
8122 /*
8123 Write the content of str into file
8124
8125 SYNOPSIS
8126 str_to_file
8127 fname - name of file to truncate/create and write to
8128 str - content to write to file
8129 size - size of content witten to file
8130 */
8131
8132 52114 void str_to_file(const char *fname, char *str, size_t size) {
8133 52114 str_to_file2(fname, str, size, false);
8134 52114 }
8135
8136 #ifdef _WIN32
8137
8138 typedef Prealloced_array<const char *, 16> Patterns;
8139 Patterns *patterns;
8140
8141 /*
8142 init_win_path_patterns
8143
8144 DESCRIPTION
8145 Setup string patterns that will be used to detect filenames that
8146 needs to be converted from Win to Unix format
8147
8148 */
8149
8150 void init_win_path_patterns() {
8151 /* List of string patterns to match in order to find paths */
8152 const char *paths[] = {
8153 "$MYSQL_TEST_DIR", "$MYSQL_TMP_DIR", "$MYSQLTEST_VARDIR",
8154 "$MASTER_MYSOCK", "$MYSQL_SHAREDIR", "$MYSQL_CHARSETSDIR",
8155 "$MYSQL_LIBDIR", "./test/", ".ibd",
8156 ".\\ibdata", ".\\ibtmp", ".\\undo"};
8157 int num_paths = sizeof(paths) / sizeof(char *);
8158 int i;
8159 char *p;
8160
8161 DBUG_TRACE;
8162
8163 patterns = new Patterns(PSI_NOT_INSTRUMENTED);
8164
8165 /* Loop through all paths in the array */
8166 for (i = 0; i < num_paths; i++) {
8167 VAR *v;
8168 if (*(paths[i]) == '$') {
8169 v = var_get(paths[i], 0, 0, 0);
8170 p = my_strdup(PSI_NOT_INSTRUMENTED, v->str_val, MYF(MY_FAE));
8171 } else
8172 p = my_strdup(PSI_NOT_INSTRUMENTED, paths[i], MYF(MY_FAE));
8173
8174 /* Don't insert zero length strings in patterns array */
8175 if (std::strlen(p) == 0) {
8176 my_free(p);
8177 continue;
8178 }
8179
8180 if (patterns->push_back(p)) die("Out of memory");
8181
8182 DBUG_PRINT("info", ("p: %s", p));
8183 while (*p) {
8184 if (*p == '/') *p = '\\';
8185 p++;
8186 }
8187 }
8188 }
8189
8190 void free_win_path_patterns() {
8191 uint i = 0;
8192 const char **pat;
8193 for (pat = patterns->begin(); pat != patterns->end(); ++pat) {
8194 my_free(const_cast<char *>(*pat));
8195 }
8196 delete patterns;
8197 patterns = NULL;
8198 }
8199
8200 /*
8201 fix_win_paths
8202
8203 DESCRIPTION
8204 Search the string 'val' for the patterns that are known to be
8205 strings that contain filenames. Convert all \ to / in the
8206 filenames that are found.
8207
8208 Ex:
8209 val = 'Error "c:\mysql\mysql-test\var\test\t1.frm" didn't exist'
8210 => $MYSQL_TEST_DIR is found by strstr
8211 => all \ from c:\mysql\m... until next space is converted into /
8212 */
8213
8214 void fix_win_paths(const char *val, size_t len) {
8215 DBUG_TRACE;
8216 const char **pat;
8217 for (pat = patterns->begin(); pat != patterns->end(); ++pat) {
8218 char *p;
8219 DBUG_PRINT("info", ("pattern: %s", *pat));
8220
8221 /* Find and fix each path in this string */
8222 p = const_cast<char *>(val);
8223 while (p = strstr(p, *pat)) {
8224 DBUG_PRINT("info", ("Found %s in val p: %s", *pat, p));
8225 /* Found the pattern. Back up to the start of this path */
8226 while (p > val && !my_isspace(charset_info, *(p - 1))) {
8227 p--;
8228 }
8229
8230 while (*p && !my_isspace(charset_info, *p)) {
8231 if (*p == '\\') *p = '/';
8232 p++;
8233 }
8234 DBUG_PRINT("info", ("Converted \\ to / in %s", val));
8235 }
8236 }
8237 DBUG_PRINT("exit", (" val: %s, len: %d", val, len));
8238 }
8239 #endif
8240
8241 /*
8242 Append the result for one field to the dynamic string ds
8243 */
8244
8245 361680960 static void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD *field,
8246 char *val, size_t len, bool is_null) {
8247 361680960 char null[] = "NULL";
8248
8249
4/4
✓ Branch 0 taken 406545 times.
✓ Branch 1 taken 361274415 times.
✓ Branch 2 taken 180843 times.
✓ Branch 3 taken 225702 times.
361680960 if (col_idx < max_replace_column && replace_column[col_idx]) {
8250 180843 val = replace_column[col_idx];
8251 180843 len = std::strlen(val);
8252
2/2
✓ Branch 0 taken 47932602 times.
✓ Branch 1 taken 313567515 times.
361500117 } else if (is_null) {
8253 47932602 val = null;
8254 47932602 len = 4;
8255 }
8256 #ifdef _WIN32
8257 else if ((field->type == MYSQL_TYPE_DOUBLE ||
8258 field->type == MYSQL_TYPE_FLOAT) &&
8259 field->decimals >= 31) {
8260 /* Convert 1.2e+018 to 1.2e+18 and 1.2e-018 to 1.2e-18 */
8261 char *start = strchr(val, 'e');
8262 if (start && std::strlen(start) >= 5 &&
8263 (start[1] == '-' || start[1] == '+') && start[2] == '0') {
8264 start += 2; /* Now points at first '0' */
8265 if (field->flags & ZEROFILL_FLAG) {
8266 /* Move all chars before the first '0' one step right */
8267 memmove(val + 1, val, start - val);
8268 *val = '0';
8269 } else {
8270 /* Move all chars after the first '0' one step left */
8271 memmove(start, start + 1, std::strlen(start));
8272 len--;
8273 }
8274 }
8275 }
8276 #endif
8277
8278
2/2
✓ Branch 0 taken 361335287 times.
✓ Branch 1 taken 345673 times.
361680960 if (!display_result_vertically) {
8279
3/4
✓ Branch 0 taken 294629327 times.
✓ Branch 1 taken 66705960 times.
✓ Branch 2 taken 294629327 times.
✗ Branch 3 not taken.
361335287 if (col_idx) dynstr_append_mem(ds, "\t", 1);
8280
1/2
✓ Branch 0 taken 361335287 times.
✗ Branch 1 not taken.
361335287 replace_dynstr_append_mem(ds, val, len);
8281 } else {
8282
1/2
✓ Branch 0 taken 345673 times.
✗ Branch 1 not taken.
345673 dynstr_append(ds, field->name);
8283
1/2
✓ Branch 0 taken 345673 times.
✗ Branch 1 not taken.
345673 dynstr_append_mem(ds, "\t", 1);
8284
1/2
✓ Branch 0 taken 345673 times.
✗ Branch 1 not taken.
345673 replace_dynstr_append_mem(ds, val, len);
8285
1/2
✓ Branch 0 taken 345673 times.
✗ Branch 1 not taken.
345673 dynstr_append_mem(ds, "\n", 1);
8286 }
8287 361680960 }
8288
8289 /*
8290 Append all results to the dynamic string separated with '\t'
8291 Values may be converted with 'replace_column'
8292 */
8293
8294 1244283 static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) {
8295 MYSQL_ROW row;
8296 1244283 uint num_fields = mysql_num_fields(res);
8297 1244283 MYSQL_FIELD *fields = mysql_fetch_fields(res);
8298 ulong *lengths;
8299
8300
2/2
✓ Branch 0 taken 66716644 times.
✓ Branch 1 taken 1244283 times.
67960927 while ((row = mysql_fetch_row_wrapper(res))) {
8301 uint i;
8302 66716644 lengths = mysql_fetch_lengths(res);
8303
2/2
✓ Branch 0 taken 361680960 times.
✓ Branch 1 taken 66716644 times.
428397604 for (i = 0; i < num_fields; i++) {
8304 /* looks ugly , but put here to convince parfait */
8305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 361680960 times.
361680960 assert(lengths);
8306 361680960 append_field(ds, i, &fields[i], row[i], lengths[i], !row[i]);
8307 }
8308
2/2
✓ Branch 0 taken 66705960 times.
✓ Branch 1 taken 10684 times.
66716644 if (!display_result_vertically) dynstr_append_mem(ds, "\n", 1);
8309 }
8310 1244283 }
8311
8312 /*
8313 Append all results from ps execution to the dynamic string separated
8314 with '\t'. Values may be converted with 'replace_column'
8315 */
8316
8317 static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
8318 MYSQL_FIELD *fields, uint num_fields) {
8319 MYSQL_BIND *my_bind;
8320 bool *is_null;
8321 ulong *length;
8322 uint i;
8323
8324 /* Allocate array with bind structs, lengths and NULL flags */
8325 my_bind = (MYSQL_BIND *)my_malloc(PSI_NOT_INSTRUMENTED,
8326 num_fields * sizeof(MYSQL_BIND),
8327 MYF(MY_WME | MY_FAE | MY_ZEROFILL));
8328 length = (ulong *)my_malloc(PSI_NOT_INSTRUMENTED, num_fields * sizeof(ulong),
8329 MYF(MY_WME | MY_FAE));
8330 is_null = (bool *)my_malloc(PSI_NOT_INSTRUMENTED, num_fields * sizeof(bool),
8331 MYF(MY_WME | MY_FAE));
8332
8333 /* Allocate data for the result of each field */
8334 for (i = 0; i < num_fields; i++) {
8335 size_t max_length = fields[i].max_length + 1;
8336 my_bind[i].buffer_type = MYSQL_TYPE_STRING;
8337 my_bind[i].buffer =
8338 my_malloc(PSI_NOT_INSTRUMENTED, max_length, MYF(MY_WME | MY_FAE));
8339 my_bind[i].buffer_length = static_cast<ulong>(max_length);
8340 my_bind[i].is_null = &is_null[i];
8341 my_bind[i].length = &length[i];
8342
8343 DBUG_PRINT("bind", ("col[%d]: buffer_type: %d, buffer_length: %lu", i,
8344 my_bind[i].buffer_type, my_bind[i].buffer_length));
8345 }
8346
8347 if (mysql_stmt_bind_result(stmt, my_bind))
8348 die("mysql_stmt_bind_result failed: %d: %s", mysql_stmt_errno(stmt),
8349 mysql_stmt_error(stmt));
8350
8351 while (mysql_stmt_fetch(stmt) == 0) {
8352 for (i = 0; i < num_fields; i++)
8353 append_field(ds, i, &fields[i], (char *)my_bind[i].buffer,
8354 *my_bind[i].length, *my_bind[i].is_null);
8355 if (!display_result_vertically) dynstr_append_mem(ds, "\n", 1);
8356 }
8357
8358 int rc;
8359 if ((rc = mysql_stmt_fetch(stmt)) != MYSQL_NO_DATA)
8360 die("fetch didn't end with MYSQL_NO_DATA from statement: %d: %s; rc=%d",
8361 mysql_stmt_errno(stmt), mysql_stmt_error(stmt), rc);
8362
8363 for (i = 0; i < num_fields; i++) {
8364 /* Free data for output */
8365 my_free(my_bind[i].buffer);
8366 }
8367 /* Free array with bind structs, lengths and NULL flags */
8368 my_free(my_bind);
8369 my_free(length);
8370 my_free(is_null);
8371 }
8372
8373 /*
8374 Append metadata for fields to output
8375 */
8376
8377 336 static void append_metadata(DYNAMIC_STRING *ds, MYSQL_FIELD *field,
8378 uint num_fields) {
8379 MYSQL_FIELD *field_end;
8380 336 dynstr_append(ds,
8381 "Catalog\tDatabase\tTable\tTable_alias\tColumn\t"
8382 "Column_alias\tType\tLength\tMax length\tIs_null\t"
8383 "Flags\tDecimals\tCharsetnr\n");
8384
8385
2/2
✓ Branch 0 taken 3739 times.
✓ Branch 1 taken 336 times.
4075 for (field_end = field + num_fields; field < field_end; field++) {
8386 3739 dynstr_append_mem(ds, field->catalog, field->catalog_length);
8387 3739 dynstr_append_mem(ds, "\t", 1);
8388 3739 dynstr_append_mem(ds, field->db, field->db_length);
8389 3739 dynstr_append_mem(ds, "\t", 1);
8390 3739 dynstr_append_mem(ds, field->org_table, field->org_table_length);
8391 3739 dynstr_append_mem(ds, "\t", 1);
8392 3739 dynstr_append_mem(ds, field->table, field->table_length);
8393 3739 dynstr_append_mem(ds, "\t", 1);
8394 3739 dynstr_append_mem(ds, field->org_name, field->org_name_length);
8395 3739 dynstr_append_mem(ds, "\t", 1);
8396 3739 dynstr_append_mem(ds, field->name, field->name_length);
8397 3739 dynstr_append_mem(ds, "\t", 1);
8398 3739 replace_dynstr_append_uint(ds, field->type);
8399 3739 dynstr_append_mem(ds, "\t", 1);
8400 3739 replace_dynstr_append_uint(ds, field->length);
8401 3739 dynstr_append_mem(ds, "\t", 1);
8402 3739 replace_dynstr_append_uint(ds, field->max_length);
8403 3739 dynstr_append_mem(ds, "\t", 1);
8404
2/2
✓ Branch 0 taken 395 times.
✓ Branch 1 taken 3344 times.
3739 dynstr_append_mem(ds, (IS_NOT_NULL(field->flags) ? "N" : "Y"), 1);
8405 3739 dynstr_append_mem(ds, "\t", 1);
8406 3739 replace_dynstr_append_uint(ds, field->flags);
8407 3739 dynstr_append_mem(ds, "\t", 1);
8408 3739 replace_dynstr_append_uint(ds, field->decimals);
8409 3739 dynstr_append_mem(ds, "\t", 1);
8410 3739 replace_dynstr_append_uint(ds, field->charsetnr);
8411 3739 dynstr_append_mem(ds, "\n", 1);
8412 }
8413 336 }
8414
8415 /*
8416 Append affected row count and other info to output
8417 */
8418
8419 3030 static void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows,
8420 const char *info) {
8421 char buf[40], buff2[21];
8422
1/2
✓ Branch 0 taken 3030 times.
✗ Branch 1 not taken.
3030 sprintf(buf, "affected rows: %s\n", llstr(affected_rows, buff2));
8423
1/2
✓ Branch 0 taken 3030 times.
✗ Branch 1 not taken.
3030 dynstr_append(ds, buf);
8424
2/2
✓ Branch 0 taken 1591 times.
✓ Branch 1 taken 1439 times.
3030 if (info) {
8425
1/2
✓ Branch 0 taken 1591 times.
✗ Branch 1 not taken.
1591 dynstr_append(ds, "info: ");
8426
1/2
✓ Branch 0 taken 1591 times.
✗ Branch 1 not taken.
1591 dynstr_append(ds, info);
8427
1/2
✓ Branch 0 taken 1591 times.
✗ Branch 1 not taken.
1591 dynstr_append_mem(ds, "\n", 1);
8428 }
8429 3030 }
8430
8431 /**
8432 @brief Append state change information (received through Ok packet) to the
8433 output.
8434
8435 @param [in,out] ds Dynamic string to hold the content to be printed.
8436 @param [in] mysql Connection handle.
8437 */
8438
8439 588 static void append_session_track_info(DYNAMIC_STRING *ds, MYSQL *mysql) {
8440
2/2
✓ Branch 0 taken 3528 times.
✓ Branch 1 taken 588 times.
4116 for (unsigned int type = SESSION_TRACK_BEGIN; type <= SESSION_TRACK_END;
8441 type++) {
8442 const char *data;
8443 size_t data_length;
8444
8445
3/4
✓ Branch 0 taken 3528 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 387 times.
✓ Branch 3 taken 3141 times.
3528 if (!mysql_session_track_get_first(mysql, (enum_session_state_type)type,
8446 &data, &data_length)) {
8447 /*
8448 Append the type information. Please update the definition of APPEND_TYPE
8449 when any changes are made to enum_session_state_type.
8450 */
8451
13/23
✓ Branch 0 taken 387 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 53 times.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 86 times.
✓ Branch 5 taken 29 times.
✓ Branch 6 taken 78 times.
✓ Branch 7 taken 125 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 53 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 16 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 86 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 29 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 78 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 125 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
387 APPEND_TYPE(type);
8452
1/2
✓ Branch 0 taken 387 times.
✗ Branch 1 not taken.
387 dynstr_append(ds, "-- ");
8453
1/2
✓ Branch 0 taken 387 times.
✗ Branch 1 not taken.
387 dynstr_append_mem(ds, data, data_length);
8454 } else
8455 3141 continue;
8456
3/4
✓ Branch 0 taken 478 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 91 times.
✓ Branch 3 taken 387 times.
478 while (!mysql_session_track_get_next(mysql, (enum_session_state_type)type,
8457 &data, &data_length)) {
8458
1/2
✓ Branch 0 taken 91 times.
✗ Branch 1 not taken.
91 dynstr_append(ds, "\n-- ");
8459
1/2
✓ Branch 0 taken 91 times.
✗ Branch 1 not taken.
91 dynstr_append_mem(ds, data, data_length);
8460 }
8461
1/2
✓ Branch 0 taken 387 times.
✗ Branch 1 not taken.
387 dynstr_append(ds, "\n\n");
8462 }
8463 588 }
8464
8465 /*
8466 Display the table headings with the names tab separated
8467 */
8468
8469 1161637 static void append_table_headings(DYNAMIC_STRING *ds, MYSQL_FIELD *field,
8470 uint num_fields) {
8471 uint col_idx;
8472
2/2
✓ Branch 0 taken 7768516 times.
✓ Branch 1 taken 1161637 times.
8930153 for (col_idx = 0; col_idx < num_fields; col_idx++) {
8473
2/2
✓ Branch 0 taken 6606879 times.
✓ Branch 1 taken 1161637 times.
7768516 if (col_idx) dynstr_append_mem(ds, "\t", 1);
8474 7768516 replace_dynstr_append(ds, field[col_idx].name);
8475 }
8476 1161637 dynstr_append_mem(ds, "\n", 1);
8477 1161637 }
8478
8479 /// Check whether a given warning is in list of disabled or enabled warnings.
8480 ///
8481 /// @param warnings List of either disabled or enabled warnings.
8482 /// @param error Error number
8483 /// @param warning_found Boolean value, should be set to true if warning
8484 /// is found in the list, false otherwise.
8485 ///
8486 /// @retval True if the given warning is present in the list, and
8487 /// ignore flag for that warning is not set, false otherwise.
8488 1522 static bool match_warnings(Expected_warnings *warnings, std::uint32_t error,
8489 bool *warning_found) {
8490 1522 bool match_found = false;
8491 1522 std::vector<std::unique_ptr<Warning>>::iterator warning = warnings->begin();
8492
8493
2/2
✓ Branch 0 taken 1608 times.
✓ Branch 1 taken 49 times.
1657 for (; warning != warnings->end(); warning++) {
8494
2/2
✓ Branch 0 taken 1475 times.
✓ Branch 1 taken 133 times.
1608 if ((*warning)->warning_code() == error) {
8495 1475 *warning_found = true;
8496
2/2
✓ Branch 0 taken 1473 times.
✓ Branch 1 taken 2 times.
1475 if (!(*warning)->ignore_warning()) {
8497 1473 match_found = true;
8498 1473 break;
8499 }
8500 }
8501 }
8502
8503 1522 return match_found;
8504 }
8505
8506 /// Handle one warning which occurred during execution of a query.
8507 ///
8508 /// @param ds DYNAMIC_STRING object to store the warnings.
8509 /// @param warning Warning string
8510 ///
8511 /// @retval True if a warning is found in the list of disabled or enabled
8512 /// warnings, false otherwise.
8513 1522 static bool handle_one_warning(DYNAMIC_STRING *ds, std::string warning) {
8514 // Each line of show warnings output contains information about
8515 // error level, error code and the error/warning message separated
8516 // by '\t'. Parse each line from the show warnings output to
8517 // extract the error code and compare it with list of expected
8518 // warnings.
8519 1522 bool warning_found = false;
8520 1522 std::string error_code;
8521
1/2
✓ Branch 0 taken 1522 times.
✗ Branch 1 not taken.
1522 std::stringstream warn_msg(warning);
8522
8523
4/6
✓ Branch 0 taken 6088 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6088 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4566 times.
✓ Branch 5 taken 1522 times.
6088 while (std::getline(warn_msg, error_code, '\t')) {
8524
1/2
✓ Branch 0 taken 4566 times.
✗ Branch 1 not taken.
4566 int errcode = get_int_val(error_code.c_str());
8525
2/2
✓ Branch 0 taken 1522 times.
✓ Branch 1 taken 3044 times.
4566 if (errcode != -1) {
8526
2/2
✓ Branch 0 taken 1468 times.
✓ Branch 1 taken 54 times.
1522 if (disabled_warnings->count()) {
8527 // Print the warning if it doesn't match with any of the
8528 // disabled warnings.
8529
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 1438 times.
1468 if (!match_warnings(disabled_warnings, errcode, &warning_found)) {
8530
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 dynstr_append_mem(ds, warning.c_str(), warning.length());
8531
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 dynstr_append_mem(ds, "\n", 1);
8532 }
8533
1/2
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
54 } else if (enabled_warnings->count()) {
8534
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 19 times.
54 if (match_warnings(enabled_warnings, errcode, &warning_found)) {
8535
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35 dynstr_append_mem(ds, warning.c_str(), warning.length());
8536
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35 dynstr_append_mem(ds, "\n", 1);
8537 }
8538 }
8539 }
8540 }
8541
8542 1522 return warning_found;
8543 1522 }
8544
8545 /// Handle warnings which occurred during execution of a query.
8546 ///
8547 /// @param ds DYNAMIC_STRING object to store the warnings.
8548 /// @param ds_warnings String containing all the generated warnings.
8549 1314 static void handle_warnings(DYNAMIC_STRING *ds, const char *ds_warnings) {
8550 1314 bool warning_found = false;
8551 1314 std::string warning;
8552
2/4
✓ Branch 0 taken 1314 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1314 times.
✗ Branch 3 not taken.
2628 std::stringstream warnings(ds_warnings);
8553
8554 // Set warning_found only if at least one of the warning exist
8555 // in expected list of warnings.
8556
4/6
✓ Branch 0 taken 2836 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2836 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1522 times.
✓ Branch 5 taken 1314 times.
2836 while (std::getline(warnings, warning))
8557
4/6
✓ Branch 0 taken 1522 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1522 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1475 times.
✓ Branch 5 taken 47 times.
1522 if (handle_one_warning(ds, warning)) warning_found = true;
8558
8559 // Throw an error and abort the test run if a query generates warnings
8560 // which are not listed as expected.
8561
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1312 times.
1314 if (!warning_found) {
8562 2 std::string warning_list;
8563
8564
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (disabled_warnings->count())
8565
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 warning_list = disabled_warnings->warnings_list();
8566 else if (enabled_warnings->count())
8567 warning_list = enabled_warnings->warnings_list();
8568
8569 2 die("Query '%s' didn't generate any of the expected warning(s) '%s'.",
8570 curr_command->query, warning_list.c_str());
8571 }
8572 1312 }
8573
8574 /// Fetch warnings generated by server while executing a query and
8575 /// append them to warnings buffer 'ds'.
8576 ///
8577 /// @param ds DYNAMIC_STRING object to store the warnings
8578 /// @param mysql mysql handle object
8579 ///
8580 /// @retval Number of warnings appended to ds
8581 8209849 static int append_warnings(DYNAMIC_STRING *ds, MYSQL *mysql) {
8582 unsigned int count;
8583
3/4
✓ Branch 0 taken 8209849 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8136049 times.
✓ Branch 3 taken 73800 times.
8209849 if (!(count = mysql_warning_count(mysql))) return 0;
8584
8585 // If one day we will support execution of multi-statements
8586 // through PS API we should not issue SHOW WARNINGS until
8587 // we have not read all results.
8588
2/4
✓ Branch 0 taken 73800 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 73800 times.
73800 assert(!mysql_more_results(mysql));
8589
8590 MYSQL_RES *warn_res;
8591
2/4
✓ Branch 0 taken 73800 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 73800 times.
73800 if (mysql_real_query_wrapper(mysql, "SHOW WARNINGS", 13))
8592 die("Error running query \"SHOW WARNINGS\": %s", mysql_error(mysql));
8593
8594
2/4
✓ Branch 0 taken 73800 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 73800 times.
73800 if (!(warn_res = mysql_store_result_wrapper(mysql)))
8595 die("Warning count is %u but didn't get any warnings", count);
8596
8597 DYNAMIC_STRING ds_warnings;
8598
1/2
✓ Branch 0 taken 73800 times.
✗ Branch 1 not taken.
73800 init_dynamic_string(&ds_warnings, "", 1024);
8599
1/2
✓ Branch 0 taken 73800 times.
✗ Branch 1 not taken.
73800 append_result(&ds_warnings, warn_res);
8600
1/2
✓ Branch 0 taken 73800 times.
✗ Branch 1 not taken.
73800 mysql_free_result_wrapper(warn_res);
8601
8602
6/6
✓ Branch 0 taken 1314 times.
✓ Branch 1 taken 72486 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 1300 times.
✓ Branch 4 taken 1314 times.
✓ Branch 5 taken 72486 times.
75128 if (disable_warnings &&
8603
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
1328 (disabled_warnings->count() || enabled_warnings->count()))
8604
1/2
✓ Branch 0 taken 1312 times.
✗ Branch 1 not taken.
1314 handle_warnings(ds, ds_warnings.str);
8605
1/2
✓ Branch 0 taken 72486 times.
✗ Branch 1 not taken.
72486 else if (!disable_warnings)
8606
1/2
✓ Branch 0 taken 72486 times.
✗ Branch 1 not taken.
72486 dynstr_append_mem(ds, ds_warnings.str, ds_warnings.length);
8607
8608
1/2
✓ Branch 0 taken 73798 times.
✗ Branch 1 not taken.
73798 dynstr_free(&ds_warnings);
8609 73798 return ds->length;
8610 }
8611
8612 /// Run query using MySQL C API
8613 ///
8614 /// @param cn Connection object
8615 /// @param command Pointer to the st_command structure which holds the
8616 /// arguments and information for the command.
8617 /// @param flags Flags indicating if we should SEND and/or REAP.
8618 /// @param query Query string
8619 /// @param query_len Length of the query string
8620 /// @param ds Output buffer to store the query result.
8621 /// @param ds_warnings Buffer to store the warnings generated while
8622 /// executing the query.
8623 10516220 static void run_query_normal(struct st_connection *cn,
8624 struct st_command *command, int flags,
8625 const char *query, size_t query_len,
8626 DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings) {
8627 10516220 int error = 0;
8628 10516220 std::uint32_t counter = 0;
8629 10516220 MYSQL *mysql = &cn->mysql;
8630 10516220 MYSQL_RES *res = nullptr;
8631
8632
2/2
✓ Branch 0 taken 10409725 times.
✓ Branch 1 taken 106495 times.
10516220 if (flags & QUERY_SEND_FLAG) {
8633 /* Send the query */
8634
2/2
✓ Branch 0 taken 1648047 times.
✓ Branch 1 taken 8761678 times.
10409725 if (mysql_send_query_wrapper(&cn->mysql, query,
8635 static_cast<ulong>(query_len))) {
8636 1648047 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
8637 mysql_sqlstate(mysql), ds);
8638 1648044 goto end;
8639 }
8640 }
8641
8642
2/2
✓ Branch 0 taken 108971 times.
✓ Branch 1 taken 8759202 times.
8868173 if (!(flags & QUERY_REAP_FLAG)) {
8643 108971 cn->pending = true;
8644 108971 return;
8645 }
8646
8647 do {
8648 /*
8649 When on first result set, call mysql_read_query_result_wrapper to
8650 retrieve answer to the query sent earlier
8651 */
8652
6/6
✓ Branch 0 taken 8759202 times.
✓ Branch 1 taken 844810 times.
✓ Branch 2 taken 90299 times.
✓ Branch 3 taken 8668903 times.
✓ Branch 4 taken 90299 times.
✓ Branch 5 taken 9513713 times.
9604012 if ((counter == 0) && mysql_read_query_result_wrapper(&cn->mysql)) {
8653 /* we've failed to collect the result set */
8654 90299 cn->pending = true;
8655 90299 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
8656 mysql_sqlstate(mysql), ds);
8657 90288 goto end;
8658 }
8659
8660 /*
8661 Store the result of the query if it will return any fields
8662 */
8663
4/4
✓ Branch 0 taken 1255287 times.
✓ Branch 1 taken 8258426 times.
✓ Branch 2 taken 4407 times.
✓ Branch 3 taken 9509306 times.
10769000 if (mysql_field_count(mysql) &&
8664
2/2
✓ Branch 0 taken 4407 times.
✓ Branch 1 taken 1250880 times.
1255287 ((res = mysql_store_result_wrapper(mysql)) == nullptr)) {
8665 4407 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
8666 mysql_sqlstate(mysql), ds);
8667 4407 goto end;
8668 }
8669
8670
2/2
✓ Branch 0 taken 9190748 times.
✓ Branch 1 taken 318558 times.
9509306 if (!disable_result_log) {
8671
2/2
✓ Branch 0 taken 1170483 times.
✓ Branch 1 taken 8020265 times.
9190748 if (res) {
8672 1170483 MYSQL_FIELD *fields = mysql_fetch_fields(res);
8673 1170483 std::uint32_t num_fields = mysql_num_fields(res);
8674
8675
2/2
✓ Branch 0 taken 336 times.
✓ Branch 1 taken 1170147 times.
1170483 if (display_metadata) append_metadata(ds, fields, num_fields);
8676
8677
2/2
✓ Branch 0 taken 1161637 times.
✓ Branch 1 taken 8846 times.
1170483 if (!display_result_vertically)
8678 1161637 append_table_headings(ds, fields, num_fields);
8679
8680 1170483 append_result(ds, res);
8681 }
8682
8683 // Need to call mysql_affected_rows() before the "new"
8684 // query to find the warnings.
8685
2/2
✓ Branch 0 taken 3030 times.
✓ Branch 1 taken 9187718 times.
9190748 if (!disable_info)
8686 3030 append_info(ds, mysql_affected_rows(mysql), mysql_info(mysql));
8687
8688
2/2
✓ Branch 0 taken 588 times.
✓ Branch 1 taken 9190160 times.
9190748 if (display_session_track_info) append_session_track_info(ds, mysql);
8689
8690 // Add all warnings to the result. We can't do this if we are in
8691 // the middle of processing results from multi-statement, because
8692 // this will break protocol.
8693
4/4
✓ Branch 0 taken 139978 times.
✓ Branch 1 taken 1545 times.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 139958 times.
281501 if ((!disable_warnings || disabled_warnings->count() ||
8694
4/4
✓ Branch 0 taken 141523 times.
✓ Branch 1 taken 9049225 times.
✓ Branch 2 taken 8209849 times.
✓ Branch 3 taken 980899 times.
18521474 enabled_warnings->count()) &&
8695
2/2
✓ Branch 0 taken 8209849 times.
✓ Branch 1 taken 840941 times.
9050790 !mysql_more_results(mysql)) {
8696
5/6
✓ Branch 0 taken 8137343 times.
✓ Branch 1 taken 72504 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8137343 times.
✓ Branch 4 taken 72504 times.
✓ Branch 5 taken 8137343 times.
8209849 if (append_warnings(ds_warnings, mysql) || ds_warnings->length) {
8697 72504 dynstr_append_mem(ds, "Warnings:\n", 10);
8698 72504 dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length);
8699 }
8700 }
8701 }
8702
8703
2/2
✓ Branch 0 taken 1250880 times.
✓ Branch 1 taken 8258424 times.
9509304 if (res) {
8704 1250880 mysql_free_result_wrapper(res);
8705 1250880 res = nullptr;
8706 }
8707 9509304 counter++;
8708
2/2
✓ Branch 0 taken 844810 times.
✓ Branch 1 taken 8664494 times.
9509304 } while (!(error = mysql_next_result_wrapper(mysql)));
8709
2/2
✓ Branch 0 taken 151 times.
✓ Branch 1 taken 8664343 times.
8664494 if (error > 0) {
8710 // We got an error from mysql_next_result, maybe expected.
8711 151 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
8712 mysql_sqlstate(mysql), ds);
8713 149 goto end;
8714 }
8715
8716 // Successful and there are no more results.
8717
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8664343 times.
8664343 assert(error == -1);
8718
8719 // If we come here the query is both executed and read successfully.
8720 8664343 handle_no_error(command);
8721 8664342 revert_properties();
8722
8723 10407230 end:
8724 10407230 cn->pending = false;
8725
8726 // We save the return code (mysql_errno(mysql)) from the last call sent
8727 // to the server into the mysqltest builtin variable $mysql_errno. This
8728 // variable then can be used from the test case itself.
8729 10407230 var_set_errno(mysql_errno(mysql));
8730 }
8731
8732 /// Run query using prepared statement C API
8733 ///
8734 /// @param mysql mysql handle
8735 /// @param command Pointer to the st_command structure which holds the
8736 /// arguments and information for the command.
8737 /// @param query Query string
8738 /// @param query_len Length of the query string
8739 /// @param ds Output buffer to store the query result.
8740 /// @param ds_warnings Buffer to store the warnings generated while
8741 /// executing the query.
8742 static void run_query_stmt(MYSQL *mysql, struct st_command *command,
8743 const char *query, size_t query_len,
8744 DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings) {
8745 // Init a new stmt if it's not already one created for this connection.
8746 MYSQL_STMT *stmt;
8747 if (!(stmt = cur_con->stmt)) {
8748 if (!(stmt = mysql_stmt_init(mysql))) die("unable to init stmt structure");
8749 cur_con->stmt = stmt;
8750 }
8751
8752 DYNAMIC_STRING ds_prepare_warnings;
8753 DYNAMIC_STRING ds_execute_warnings;
8754
8755 // Init dynamic strings for warnings.
8756 if (!disable_warnings || disabled_warnings->count() ||
8757 enabled_warnings->count()) {
8758 init_dynamic_string(&ds_prepare_warnings, nullptr, 0);
8759 init_dynamic_string(&ds_execute_warnings, nullptr, 0);
8760 }
8761
8762 // Note that here 'res' is meta data result set
8763 MYSQL_RES *res = nullptr;
8764 int err = 0;
8765
8766 // Prepare the query
8767 if (mysql_stmt_prepare(stmt, query, static_cast<ulong>(query_len))) {
8768 handle_error(command, mysql_stmt_errno(stmt), mysql_stmt_error(stmt),
8769 mysql_stmt_sqlstate(stmt), ds);
8770 goto end;
8771 }
8772
8773 // Get the warnings from mysql_stmt_prepare and keep them in a
8774 // separate string.
8775 if (!disable_warnings || disabled_warnings->count() ||
8776 enabled_warnings->count())
8777 append_warnings(&ds_prepare_warnings, mysql);
8778
8779 // No need to call mysql_stmt_bind_param() because we have no
8780 // parameter markers.
8781 if (cursor_protocol_enabled) {
8782 // Use cursor when retrieving result.
8783 unsigned long type = CURSOR_TYPE_READ_ONLY;
8784 if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void *)&type))
8785 die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s",
8786 mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
8787 }
8788
8789 // Execute the query
8790 if (mysql_stmt_execute(stmt)) {
8791 handle_error(command, mysql_stmt_errno(stmt), mysql_stmt_error(stmt),
8792 mysql_stmt_sqlstate(stmt), ds);
8793 goto end;
8794 }
8795
8796 // When running in cursor_protocol get the warnings from execute here
8797 // and keep them in a separate string for later.
8798 if (cursor_protocol_enabled &&
8799 (!disable_warnings || disabled_warnings->count() ||
8800 enabled_warnings->count()))
8801 append_warnings(&ds_execute_warnings, mysql);
8802
8803 // We instruct that we want to update the "max_length" field in
8804 // mysql_stmt_store_result(), this is our only way to know how much
8805 // buffer to allocate for result data
8806 {
8807 bool one = true;
8808 if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void *)&one))
8809 die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s",
8810 mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
8811 }
8812
8813 do {
8814 // If we got here the statement succeeded and was expected to do so,
8815 // get data. Note that this can still give errors found during execution.
8816 // Store the result of the query if if will return any fields
8817 if (mysql_stmt_field_count(stmt) && mysql_stmt_store_result(stmt)) {
8818 handle_error(command, mysql_stmt_errno(stmt), mysql_stmt_error(stmt),
8819 mysql_stmt_sqlstate(stmt), ds);
8820 goto end;
8821 }
8822
8823 if (!disable_result_log) {
8824 // Not all statements creates a result set. If there is one we can
8825 // now create another normal result set that contains the meta
8826 // data. This set can be handled almost like any other non prepared
8827 // statement result set.
8828 if ((res = mysql_stmt_result_metadata(stmt)) != nullptr) {
8829 // Take the column count from meta info
8830 MYSQL_FIELD *fields = mysql_fetch_fields(res);
8831 std::uint32_t num_fields = mysql_num_fields(res);
8832
8833 if (display_metadata) append_metadata(ds, fields, num_fields);
8834
8835 if (!display_result_vertically)
8836 append_table_headings(ds, fields, num_fields);
8837
8838 append_stmt_result(ds, stmt, fields, num_fields);
8839
8840 // Free normal result set with meta data
8841 mysql_free_result_wrapper(res);
8842 } else {
8843 // This is a query without resultset
8844 }
8845
8846 // Fetch info before fetching warnings, since it will be reset
8847 // otherwise.
8848 if (!disable_info)
8849 append_info(ds, mysql_affected_rows(stmt->mysql), mysql_info(mysql));
8850
8851 if (display_session_track_info) append_session_track_info(ds, mysql);
8852
8853 // Add all warnings to the result. We can't do this if we are in
8854 // the middle of processing results from multi-statement, because
8855 // this will break protocol.
8856 if ((!disable_warnings || disabled_warnings->count() ||
8857 enabled_warnings->count()) &&
8858 !mysql_more_results(stmt->mysql)) {
8859 // Get the warnings from execute. Append warnings to ds,
8860 // if there are any.
8861 append_warnings(&ds_execute_warnings, mysql);
8862 if (ds_execute_warnings.length || ds_prepare_warnings.length ||
8863 ds_warnings->length) {
8864 dynstr_append_mem(ds, "Warnings:\n", 10);
8865
8866 // Append warnings if exist any
8867 if (ds_warnings->length)
8868 dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length);
8869
8870 // Append prepare warnings if exist any
8871 if (ds_prepare_warnings.length)
8872 dynstr_append_mem(ds, ds_prepare_warnings.str,
8873 ds_prepare_warnings.length);
8874
8875 // Append execute warnings if exist any
8876 if (ds_execute_warnings.length)
8877 dynstr_append_mem(ds, ds_execute_warnings.str,
8878 ds_execute_warnings.length);
8879 }
8880 }
8881 }
8882 } while ((err = mysql_stmt_next_result(stmt)) == 0);
8883
8884 if (err > 0) {
8885 // We got an error from mysql_stmt_next_result, maybe expected.
8886 handle_error(command, mysql_stmt_errno(stmt), mysql_stmt_error(stmt),
8887 mysql_stmt_sqlstate(stmt), ds);
8888 goto end;
8889 }
8890
8891 // If we got here the statement was both executed and read successfully.
8892 handle_no_error(command);
8893
8894 end:
8895 if (!disable_warnings || disabled_warnings->count() ||
8896 enabled_warnings->count()) {
8897 dynstr_free(&ds_prepare_warnings);
8898 dynstr_free(&ds_execute_warnings);
8899 }
8900 revert_properties();
8901
8902 // We save the return code (mysql_stmt_errno(stmt)) from the last call sent
8903 // to the server into the mysqltest builtin variable $mysql_errno. This
8904 // variable then can be used from the test case itself.
8905 var_set_errno(mysql_stmt_errno(stmt));
8906
8907 // Close the statement if no reconnect, need new prepare.
8908 if (mysql->reconnect) {
8909 mysql_stmt_close(stmt);
8910 cur_con->stmt = nullptr;
8911 }
8912 }
8913
8914 /*
8915 Create a util connection if one does not already exists
8916 and use that to run the query
8917 This is done to avoid implicit commit when creating/dropping objects such
8918 as view, sp etc.
8919 */
8920
8921 static int util_query(MYSQL *org_mysql, const char *query) {
8922 MYSQL *mysql;
8923 DBUG_TRACE;
8924
8925 if (!(mysql = cur_con->util_mysql)) {
8926 DBUG_PRINT("info", ("Creating util_mysql"));
8927 if (!(mysql = mysql_init(mysql))) die("Failed in mysql_init()");
8928
8929 if (opt_init_command)
8930 mysql_options(mysql, MYSQL_INIT_COMMAND, opt_init_command);
8931 if (opt_connect_timeout)
8932 mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT,
8933 (void *)&opt_connect_timeout);
8934
8935 /* enable local infile, in non-binary builds often disabled by default */
8936 mysql_options(mysql, MYSQL_OPT_LOCAL_INFILE, nullptr);
8937 safe_connect(mysql, "util", org_mysql->host, org_mysql->user,
8938 org_mysql->passwd, org_mysql->db, org_mysql->port,
8939 org_mysql->unix_socket);
8940
8941 cur_con->util_mysql = mysql;
8942 }
8943
8944 return mysql_query_wrapper(mysql, query);
8945 }
8946
8947 /*
8948 Run query
8949
8950 SYNPOSIS
8951 run_query()
8952 mysql mysql handle
8953 command current command pointer
8954
8955 flags control the phased/stages of query execution to be performed
8956 if QUERY_SEND_FLAG bit is on, the query will be sent. If QUERY_REAP_FLAG
8957 is on the result will be read - for regular query, both bits must be on
8958 */
8959
8960 10516229 static void run_query(struct st_connection *cn, struct st_command *command,
8961 int flags) {
8962 10516229 MYSQL *mysql = &cn->mysql;
8963 DYNAMIC_STRING *ds;
8964 10516229 DYNAMIC_STRING *save_ds = nullptr;
8965 DYNAMIC_STRING ds_sorted;
8966 DYNAMIC_STRING ds_warnings;
8967 DYNAMIC_STRING eval_query;
8968 const char *query;
8969 size_t query_len;
8970 10516229 bool view_created = false, sp_created = false;
8971 10516229 bool complete_query =
8972
4/4
✓ Branch 0 taken 10409734 times.
✓ Branch 1 taken 106495 times.
✓ Branch 2 taken 10300763 times.
✓ Branch 3 taken 108971 times.
10516229 ((flags & QUERY_SEND_FLAG) && (flags & QUERY_REAP_FLAG));
8973
1/2
✓ Branch 0 taken 10516229 times.
✗ Branch 1 not taken.
10516229 DBUG_TRACE;
8974
1/2
✓ Branch 0 taken 10516229 times.
✗ Branch 1 not taken.
10516229 dynstr_set(&ds_result, "");
8975
8976
4/4
✓ Branch 0 taken 106496 times.
✓ Branch 1 taken 10409733 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 106495 times.
10516229 if (cn->pending && (flags & QUERY_SEND_FLAG))
8977 1 die("Cannot run query on connection between send and reap");
8978
8979
3/4
✓ Branch 0 taken 106495 times.
✓ Branch 1 taken 10409733 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 106495 times.
10516228 if (!(flags & QUERY_SEND_FLAG) && !cn->pending)
8980 die("Cannot reap on a connection without pending send");
8981
8982 /*
8983 Evaluate query if this is an eval command
8984 */
8985
4/4
✓ Branch 0 taken 4037884 times.
✓ Branch 1 taken 6478344 times.
✓ Branch 2 taken 3958 times.
✓ Branch 3 taken 4033926 times.
10516228 if (command->type == Q_EVAL || command->type == Q_SEND_EVAL) {
8986
1/2
✓ Branch 0 taken 6482302 times.
✗ Branch 1 not taken.
6482302 init_dynamic_string(&eval_query, "", command->query_len + 256);
8987
1/2
✓ Branch 0 taken 6482302 times.
✗ Branch 1 not taken.
6482302 do_eval(&eval_query, command->query, command->end, false);
8988 6482302 query = eval_query.str;
8989 6482302 query_len = eval_query.length;
8990 } else {
8991 4033926 query = command->query;
8992 4033926 query_len = std::strlen(query);
8993 }
8994
8995 /*
8996 Create a temporary dynamic string to contain the
8997 output from this query.
8998 */
8999
2/2
✓ Branch 0 taken 1649 times.
✓ Branch 1 taken 10514579 times.
10516228 if (command->output_file[0])
9000 1649 ds = &ds_result;
9001 else
9002 10514579 ds = &ds_res;
9003
9004 /*
9005 Log the query into the output buffer
9006 */
9007
4/4
✓ Branch 0 taken 1268562 times.
✓ Branch 1 taken 9247666 times.
✓ Branch 2 taken 1262720 times.
✓ Branch 3 taken 5842 times.
10516228 if (!disable_query_log && (flags & QUERY_SEND_FLAG)) {
9008
1/2
✓ Branch 0 taken 1262712 times.
✗ Branch 1 not taken.
1262720 replace_dynstr_append_mem(ds, query, query_len);
9009
1/2
✓ Branch 0 taken 1262712 times.
✗ Branch 1 not taken.
1262712 dynstr_append_mem(ds, delimiter, delimiter_length);
9010
1/2
✓ Branch 0 taken 1262712 times.
✗ Branch 1 not taken.
1262712 dynstr_append_mem(ds, "\n", 1);
9011 }
9012
9013
3/4
✓ Branch 0 taken 4011 times.
✓ Branch 1 taken 10512209 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4011 times.
10516220 if (skip_if_hypergraph && opt_hypergraph) {
9014 constexpr char message[] =
9015 "<ignored hypergraph optimizer error: statement skipped by "
9016 "test>\n";
9017 dynstr_append_mem(&ds_res, message, strlen(message));
9018 if (command->type == Q_EVAL || command->type == Q_SEND_EVAL)
9019 dynstr_free(&eval_query);
9020 return;
9021 }
9022
9023
1/2
✓ Branch 0 taken 10516220 times.
✗ Branch 1 not taken.
10516220 init_dynamic_string(&ds_warnings, nullptr, 0);
9024 10516220 ds_warn = &ds_warnings;
9025
9026
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 10516220 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 10516220 times.
10516220 if (view_protocol_enabled && complete_query &&
9027 search_protocol_re(&view_re, query)) {
9028 /*
9029 Create the query as a view.
9030 Use replace since view can exist from a failed mysqltest run
9031 */
9032 DYNAMIC_STRING query_str;
9033 init_dynamic_string(&query_str,
9034 "CREATE OR REPLACE VIEW mysqltest_tmp_v AS ",
9035 query_len + 64);
9036 dynstr_append_mem(&query_str, query, query_len);
9037 if (util_query(mysql, query_str.str)) {
9038 /*
9039 Failed to create the view, this is not fatal
9040 just run the query the normal way
9041 */
9042 DBUG_PRINT("view_create_error",
9043 ("Failed to create view '%s': %d: %s", query_str.str,
9044 mysql_errno(mysql), mysql_error(mysql)));
9045
9046 /* Log error to create view */
9047 verbose_msg("Failed to create view '%s' %d: %s", query_str.str,
9048 mysql_errno(mysql), mysql_error(mysql));
9049 } else {
9050 /*
9051 Yes, it was possible to create this query as a view
9052 */
9053 view_created = true;
9054 query = "SELECT * FROM mysqltest_tmp_v";
9055 query_len = std::strlen(query);
9056
9057 /*
9058 Collect warnings from create of the view that should otherwise
9059 have been produced when the SELECT was executed
9060 */
9061 append_warnings(&ds_warnings, cur_con->util_mysql);
9062 }
9063
9064 dynstr_free(&query_str);
9065 }
9066
9067
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 10516220 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 10516220 times.
10516220 if (sp_protocol_enabled && complete_query &&
9068 search_protocol_re(&sp_re, query)) {
9069 /*
9070 Create the query as a stored procedure
9071 Drop first since sp can exist from a failed mysqltest run
9072 */
9073 DYNAMIC_STRING query_str;
9074 init_dynamic_string(&query_str,
9075 "DROP PROCEDURE IF EXISTS mysqltest_tmp_sp;",
9076 query_len + 64);
9077 util_query(mysql, query_str.str);
9078 dynstr_set(&query_str, "CREATE PROCEDURE mysqltest_tmp_sp()\n");
9079 dynstr_append_mem(&query_str, query, query_len);
9080 if (util_query(mysql, query_str.str)) {
9081 /*
9082 Failed to create the stored procedure for this query,
9083 this is not fatal just run the query the normal way
9084 */
9085 DBUG_PRINT("sp_create_error",
9086 ("Failed to create sp '%s': %d: %s", query_str.str,
9087 mysql_errno(mysql), mysql_error(mysql)));
9088
9089 /* Log error to create sp */
9090 verbose_msg("Failed to create sp '%s' %d: %s", query_str.str,
9091 mysql_errno(mysql), mysql_error(mysql));
9092
9093 } else {
9094 sp_created = true;
9095
9096 query = "CALL mysqltest_tmp_sp()";
9097 query_len = std::strlen(query);
9098 }
9099 dynstr_free(&query_str);
9100 }
9101
9102
2/2
✓ Branch 0 taken 31700 times.
✓ Branch 1 taken 10484520 times.
10516220 if (display_result_sorted) {
9103 /*
9104 Collect the query output in a separate string
9105 that can be sorted before it's added to the
9106 global result string
9107 */
9108
1/2
✓ Branch 0 taken 31700 times.
✗ Branch 1 not taken.
31700 init_dynamic_string(&ds_sorted, "", 1024);
9109 31700 save_ds = ds; /* Remember original ds */
9110 31700 ds = &ds_sorted;
9111 }
9112
9113 /*
9114 Find out how to run this query
9115
9116 Always run with normal C API if it's not a complete
9117 SEND + REAP
9118
9119 If it is a '?' in the query it may be a SQL level prepared
9120 statement already and we can't do it twice
9121 */
9122
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 10516220 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 10516220 times.
10516220 if (ps_protocol_enabled && complete_query &&
9123 search_protocol_re(&ps_re, query))
9124 run_query_stmt(mysql, command, query, query_len, ds, &ds_warnings);
9125 else
9126
1/2
✓ Branch 0 taken 10516201 times.
✗ Branch 1 not taken.
10516220 run_query_normal(cn, command, flags, query, query_len, ds, &ds_warnings);
9127
9128
1/2
✓ Branch 0 taken 10516201 times.
✗ Branch 1 not taken.
10516201 dynstr_free(&ds_warnings);
9129 10516201 ds_warn = nullptr;
9130
4/4
✓ Branch 0 taken 4037857 times.
✓ Branch 1 taken 6478344 times.
✓ Branch 2 taken 3958 times.
✓ Branch 3 taken 4033899 times.
10516201 if (command->type == Q_EVAL || command->type == Q_SEND_EVAL)
9131
1/2
✓ Branch 0 taken 6482302 times.
✗ Branch 1 not taken.
6482302 dynstr_free(&eval_query);
9132
9133
2/2
✓ Branch 0 taken 31700 times.
✓ Branch 1 taken 10484501 times.
10516201 if (display_result_sorted) {
9134 /* Sort the result set and append it to result */
9135
1/2
✓ Branch 0 taken 31700 times.
✗ Branch 1 not taken.
31700 dynstr_append_sorted(save_ds, &ds_sorted, start_sort_column);
9136 31700 ds = save_ds;
9137
1/2
✓ Branch 0 taken 31700 times.
✗ Branch 1 not taken.
31700 dynstr_free(&ds_sorted);
9138 }
9139
9140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10516201 times.
10516201 if (sp_created) {
9141 if (util_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp "))
9142 die("Failed to drop sp: %d: %s", mysql_errno(mysql), mysql_error(mysql));
9143 }
9144
9145
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10516201 times.
10516201 if (view_created) {
9146 if (util_query(mysql, "DROP VIEW mysqltest_tmp_v "))
9147 die("Failed to drop view: %d: %s", mysql_errno(mysql),
9148 mysql_error(mysql));
9149 }
9150
2/2
✓ Branch 0 taken 1649 times.
✓ Branch 1 taken 10514552 times.
10516201 if (command->output_file[0]) {
9151 /* An output file was specified for _this_ query */
9152
1/2
✓ Branch 0 taken 1649 times.
✗ Branch 1 not taken.
1649 str_to_file2(command->output_file, ds_result.str, ds_result.length, false);
9153 1649 command->output_file[0] = 0;
9154 }
9155
1/2
✓ Branch 0 taken 10516201 times.
✗ Branch 1 not taken.
10516201 }
9156
9157 /**
9158 Display the optimizer trace produced by the last executed statement.
9159 */
9160 10501027 static void display_opt_trace(struct st_connection *cn,
9161 struct st_command *command, int flags) {
9162
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1268292 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1268292 if (!disable_query_log && opt_trace_protocol_enabled && !cn->pending &&
9163
3/4
✓ Branch 0 taken 1268292 times.
✓ Branch 1 taken 9232735 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10501027 times.
11769319 !expected_errors->count() &&
9164 search_protocol_re(&opt_trace_re, command->query)) {
9165 st_command save_command = *command;
9166 DYNAMIC_STRING query_str;
9167 init_dynamic_string(&query_str,
9168 "SELECT trace FROM information_schema.optimizer_trace"
9169 " /* injected by --opt-trace-protocol */",
9170 128);
9171
9172 command->query = query_str.str;
9173 command->query_len = query_str.length;
9174 command->end = strend(command->query);
9175
9176 /* Sorted trace is not readable at all, don't bother to lower case */
9177 /* No need to keep old values, will be reset anyway */
9178 display_result_sorted = false;
9179 display_result_lower = false;
9180 run_query(cn, command, flags);
9181
9182 dynstr_free(&query_str);
9183 *command = save_command;
9184 }
9185 10501027 }
9186
9187 static void run_explain(struct st_connection *cn, struct st_command *command,
9188 int flags, bool json) {
9189 if ((flags & QUERY_REAP_FLAG) && !expected_errors->count() &&
9190 search_protocol_re(&explain_re, command->query)) {
9191 st_command save_command = *command;
9192 DYNAMIC_STRING query_str;
9193 DYNAMIC_STRING ds_warning_messages;
9194
9195 init_dynamic_string(&ds_warning_messages, "", 0);
9196 init_dynamic_string(&query_str, json ? "EXPLAIN FORMAT=JSON " : "EXPLAIN ",
9197 256);
9198 dynstr_append_mem(&query_str, command->query,
9199 command->end - command->query);
9200
9201 command->query = query_str.str;
9202 command->query_len = query_str.length;
9203 command->end = strend(command->query);
9204
9205 run_query(cn, command, flags);
9206
9207 dynstr_free(&query_str);
9208 dynstr_free(&ds_warning_messages);
9209
9210 *command = save_command;
9211 }
9212 }
9213
9214 207689754 static void get_command_type(struct st_command *command) {
9215 char save;
9216 uint type;
9217
1/2
✓ Branch 0 taken 207689754 times.
✗ Branch 1 not taken.
207689754 DBUG_TRACE;
9218
9219
2/2
✓ Branch 0 taken 42673958 times.
✓ Branch 1 taken 165015796 times.
207689754 if (*command->query == '}') {
9220 42673958 command->type = Q_END_BLOCK;
9221 42673958 return;
9222 }
9223
9224 165015796 save = command->query[command->first_word_len];
9225 165015796 command->query[command->first_word_len] = 0;
9226
1/2
✓ Branch 0 taken 165015796 times.
✗ Branch 1 not taken.
165015796 type = find_type(command->query, &command_typelib, FIND_TYPE_NO_PREFIX);
9227 165015796 command->query[command->first_word_len] = save;
9228
2/2
✓ Branch 0 taken 160466980 times.
✓ Branch 1 taken 4548816 times.
165015796 if (type > 0) {
9229 160466980 command->type = (enum enum_commands)type; /* Found command */
9230
9231 /*
9232 Look for case where "query" was explicitly specified to
9233 force command being sent to server
9234 */
9235
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 160466972 times.
160466980 if (type == Q_QUERY) {
9236 /* Skip the "query" part */
9237 8 command->query = command->first_argument;
9238 }
9239 } else {
9240 /* No mysqltest command matched */
9241
9242
2/2
✓ Branch 0 taken 4548815 times.
✓ Branch 1 taken 1 times.
4548816 if (command->type != Q_COMMENT_WITH_COMMAND) {
9243 /* A query that will sent to mysqld */
9244 4548815 command->type = Q_QUERY;
9245 } else {
9246 /* -- "comment" that didn't contain a mysqltest command */
9247 1 die("Found line '%s' beginning with -- that didn't contain "
9248 "a valid mysqltest command, check your syntax or "
9249 "use # if you intended to write a comment",
9250 command->query);
9251 }
9252 }
9253
2/2
✓ Branch 0 taken 165015795 times.
✓ Branch 1 taken 42673958 times.
207689753 }
9254
9255 /// Record how many milliseconds it took to execute the test file
9256 /// up until the current line and write it to .progress file.
9257 ///
9258 /// @param progress_file Logfile object to store the progress information
9259 /// @param line Line number of the progress file where the progress
9260 /// information should be recorded.
9261 static void mark_progress(Logfile *progress_file, int line) {
9262 static unsigned long long int progress_start = 0;
9263 unsigned long long int timer = timer_now();
9264
9265 if (!progress_start) progress_start = timer;
9266 timer = timer - progress_start;
9267
9268 std::string str_progress;
9269
9270 // Milliseconds since start
9271 std::string str_timer = std::to_string(timer);
9272 str_progress.append(str_timer);
9273 str_progress.append("\t");
9274
9275 // Parse the line number
9276 std::string str_line = std::to_string(line);
9277 str_progress.append(str_line);
9278 str_progress.append("\t");
9279
9280 // Filename
9281 str_progress.append(cur_file->file_name);
9282 str_progress.append(":");
9283
9284 // Line in file
9285 str_line = std::to_string(cur_file->lineno);
9286 str_progress.append(str_line);
9287 str_progress.append("\n");
9288
9289 if (progress_file->write(str_progress.c_str(), str_progress.length()) ||
9290 progress_file->flush()) {
9291 cleanup_and_exit(1);
9292 }
9293 }
9294
9295 #ifdef HAVE_STACKTRACE
9296 static void dump_backtrace() {
9297 struct st_connection *conn = cur_con;
9298
9299 fprintf(stderr, "mysqltest: ");
9300
9301 // Print the query and the line number
9302 if (start_lineno > 0) fprintf(stderr, "At line %u: ", start_lineno);
9303 fprintf(stderr, "%s\n", curr_command->query);
9304
9305 // Print the file stack
9306 if (cur_file && cur_file != file_stack) {
9307 fprintf(stderr, "In included ");
9308 print_file_stack();
9309 }
9310
9311 if (conn) fprintf(stderr, "conn->name: %s\n", conn->name);
9312
9313 fprintf(stderr, "Attempting backtrace.\n");
9314 fflush(stderr);
9315 my_print_stacktrace(nullptr, my_thread_stack_size);
9316 }
9317
9318 #else
9319 static void dump_backtrace() { fputs("Backtrace not available.\n", stderr); }
9320
9321 #endif
9322
9323 static void signal_handler(int sig) {
9324 fprintf(stderr, "mysqltest got " SIGNAL_FMT "\n", sig);
9325 dump_backtrace();
9326
9327 fprintf(stderr, "Writing a core file.\n");
9328 fflush(stderr);
9329 my_write_core(sig);
9330 #ifndef _WIN32
9331 // Shouldn't get here but just in case
9332 exit(1);
9333 #endif
9334 }
9335
9336 #ifdef _WIN32
9337
9338 LONG WINAPI exception_filter(EXCEPTION_POINTERS *exp) {
9339 __try {
9340 my_set_exception_pointers(exp);
9341 signal_handler(exp->ExceptionRecord->ExceptionCode);
9342 } __except (EXCEPTION_EXECUTE_HANDLER) {
9343 fputs("Got exception in exception handler!\n", stderr);
9344 }
9345
9346 return EXCEPTION_CONTINUE_SEARCH;
9347 }
9348
9349 static void init_signal_handling(void) {
9350 UINT mode;
9351
9352 mysqltest_thread = OpenThread(THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId());
9353 if (mysqltest_thread == NULL)
9354 die("OpenThread failed, err = %d.", GetLastError());
9355
9356 /* Set output destination of messages to the standard error stream. */
9357 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
9358 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
9359 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
9360 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
9361 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
9362 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
9363
9364 /* Do not not display the a error message box. */
9365 mode = SetErrorMode(0) | SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX;
9366 SetErrorMode(mode);
9367
9368 SetUnhandledExceptionFilter(exception_filter);
9369 }
9370
9371 /// Function to handle the stacktrace request event.
9372 ///
9373 /// - Suspend the thread running the test
9374 /// - Fetch CONTEXT record from the thread handle
9375 /// - Initialize EXCEPTION_RECORD structure
9376 /// - Use EXCEPTION_POINTERS and EXCEPTION_RECORD to set EXCEPTION_POINTERS
9377 /// structure
9378 /// - Call exception_filter() method to generate to stack trace
9379 /// - Resume the suspended test thread
9380 static void handle_wait_stacktrace_request_event() {
9381 fprintf(stderr, "Test case timeout failure.\n");
9382
9383 // Suspend the thread running the test
9384 if (SuspendThread(mysqltest_thread) == -1) {
9385 DWORD error = GetLastError();
9386 CloseHandle(mysqltest_thread);
9387 die("Error suspending thread, err = %d.\n", error);
9388 }
9389
9390 // Fetch the thread context
9391 CONTEXT test_thread_ctx = {0};
9392 test_thread_ctx.ContextFlags = CONTEXT_FULL;
9393
9394 if (GetThreadContext(mysqltest_thread, &test_thread_ctx) == FALSE) {
9395 DWORD error = GetLastError();
9396 CloseHandle(mysqltest_thread);
9397 die("Error while fetching thread conext information, err = %d.\n", error);
9398 }
9399
9400 EXCEPTION_POINTERS exp = {0};
9401 exp.ContextRecord = &test_thread_ctx;
9402
9403 // Set up an Exception record with EXCEPTION_BREAKPOINT code
9404 EXCEPTION_RECORD exc_rec = {0};
9405 exc_rec.ExceptionCode = EXCEPTION_BREAKPOINT;
9406 exp.ExceptionRecord = &exc_rec;
9407
9408 exception_filter(&exp);
9409
9410 // Resume the suspended test thread
9411 if (ResumeThread(mysqltest_thread) == -1) {
9412 DWORD error = GetLastError();
9413 CloseHandle(mysqltest_thread);
9414 die("Error resuming thread, err = %d.\n", error);
9415 }
9416
9417 my_set_exception_pointers(nullptr);
9418 }
9419
9420 /// Thread waiting for timeout event to occur. If the event occurs,
9421 /// this method will trigger signal_handler() function.
9422 static void wait_stacktrace_request_event() {
9423 DWORD wait_res = WaitForSingleObject(stacktrace_request_event, INFINITE);
9424 switch (wait_res) {
9425 case WAIT_OBJECT_0:
9426 handle_wait_stacktrace_request_event();
9427 break;
9428 default:
9429 die("Unexpected result %d from WaitForSingleObject.", wait_res);
9430 break;
9431 }
9432 CloseHandle(stacktrace_request_event);
9433 }
9434
9435 /// Create an event name from the safeprocess PID value of the form
9436 /// mysqltest[%d]stacktrace and spawn thread waiting for that event
9437 /// to occur.
9438 ///
9439 /// When this event occurs, signal_handler() method is called and
9440 /// stacktrace for the mysqltest client process is printed in the
9441 /// log file.
9442 static void create_stacktrace_request_event() {
9443 char event_name[64];
9444 std::sprintf(event_name, "mysqltest[%d]stacktrace", opt_safe_process_pid);
9445
9446 // Create an event for the signal handler
9447 if ((stacktrace_request_event = CreateEvent(NULL, TRUE, FALSE, event_name)) ==
9448 NULL)
9449 die("Failed to create timeout_event.");
9450
9451 wait_for_stacktrace_request_event_thread =
9452 std::thread(wait_stacktrace_request_event);
9453 }
9454
9455 #else /* _WIN32 */
9456
9457 55515 static void init_signal_handling(void) {
9458 struct sigaction sa;
9459
1/2
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
55515 DBUG_TRACE;
9460
9461 #ifdef HAVE_STACKTRACE
9462
1/2
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
55515 my_init_stacktrace();
9463 #endif
9464
9465 55515 sa.sa_flags = SA_RESETHAND | SA_NODEFER;
9466 55515 sigemptyset(&sa.sa_mask);
9467 55515 sigprocmask(SIG_SETMASK, &sa.sa_mask, nullptr);
9468
9469 55515 sa.sa_handler = signal_handler;
9470
9471 55515 sigaction(SIGSEGV, &sa, nullptr);
9472 55515 sigaction(SIGABRT, &sa, nullptr);
9473 #ifdef SIGBUS
9474 55515 sigaction(SIGBUS, &sa, nullptr);
9475 #endif
9476 55515 sigaction(SIGILL, &sa, nullptr);
9477 55515 sigaction(SIGFPE, &sa, nullptr);
9478 55515 }
9479
9480 #endif /* !_WIN32 */
9481
9482 55515 int main(int argc, char **argv) {
9483 struct st_command *command;
9484 55515 bool abort_flag = false;
9485 55515 int q_send_flag = 0;
9486 55515 uint command_executed = 0, last_command_executed = 0;
9487 char output_file[FN_REFLEN];
9488
1/2
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
55515 MY_INIT(argv[0]);
9489
9490 55515 output_file[0] = 0;
9491 55515 TMPDIR[0] = 0;
9492
9493
1/2
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
55515 init_signal_handling();
9494
9495 /* Init file stack */
9496 55515 memset(file_stack, 0, sizeof(file_stack));
9497 55515 file_stack_end =
9498 55515 file_stack + (sizeof(file_stack) / sizeof(struct st_test_file)) - 1;
9499 55515 cur_file = file_stack;
9500
9501 /* Init block stack */
9502 55515 memset(block_stack, 0, sizeof(block_stack));
9503 55515 block_stack_end =
9504 55515 block_stack + (sizeof(block_stack) / sizeof(struct st_block)) - 1;
9505 55515 cur_block = block_stack;
9506 55515 cur_block->ok = true; /* Outer block should always be executed */
9507 55515 cur_block->cmd = cmd_none;
9508
9509
1/2
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
55515 q_lines = new Q_lines(PSI_NOT_INSTRUMENTED);
9510
9511 55515 var_hash =
9512 new collation_unordered_map<std::string, std::unique_ptr<VAR, var_free>>(
9513
2/4
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 55515 times.
✗ Branch 3 not taken.
55515 charset_info, PSI_NOT_INSTRUMENTED);
9514
9515 {
9516 55515 char path_separator[] = {FN_LIBCHAR, 0};
9517
1/2
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
55515 var_set_string("SYSTEM_PATH_SEPARATOR", path_separator);
9518 }
9519
1/2
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
55515 var_set_string("MYSQL_SERVER_VERSION", MYSQL_SERVER_VERSION);
9520
1/2
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
55515 var_set_string("MYSQL_SYSTEM_TYPE", SYSTEM_TYPE);
9521
1/2
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
55515 var_set_string("MYSQL_MACHINE_TYPE", MACHINE_TYPE);
9522 if (sizeof(void *) == 8) {
9523
1/2
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
55515 var_set_string("MYSQL_SYSTEM_ARCHITECTURE", "64");
9524 } else {
9525 var_set_string("MYSQL_SYSTEM_ARCHITECTURE", "32");
9526 }
9527
9528 55515 memset(&master_pos, 0, sizeof(master_pos));
9529
9530 55515 parser.current_line = parser.read_lines = 0;
9531 55515 memset(&var_reg, 0, sizeof(var_reg));
9532
9533 55515 init_builtin_echo();
9534 #ifdef _WIN32
9535 is_windows = 1;
9536 init_win_path_patterns();
9537 #endif
9538
9539
1/2
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
55515 init_dynamic_string(&ds_res, "", 2048);
9540
1/2
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
55515 init_dynamic_string(&ds_result, "", 1024);
9541
9542
1/2
✓ Branch 0 taken 55515 times.
✗ Branch 1 not taken.
55515 global_attrs = new client_query_attributes();
9543
9544
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55515 parse_args(argc, argv);
9545
9546 #ifdef _WIN32
9547 // Create an event to request stack trace when timeout occurs
9548 if (opt_safe_process_pid) create_stacktrace_request_event();
9549 #endif
9550
9551 /* Init connections, allocate 1 extra as buffer + 1 for default */
9552 111012 connections = (struct st_connection *)my_malloc(
9553 PSI_NOT_INSTRUMENTED,
9554
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 (opt_max_connections + 2) * sizeof(struct st_connection),
9555 MYF(MY_WME | MY_ZEROFILL));
9556 55506 connections_end = connections + opt_max_connections + 1;
9557 55506 next_con = connections + 1;
9558
9559
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$PS_PROTOCOL", ps_protocol);
9560
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$SP_PROTOCOL", sp_protocol);
9561
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$VIEW_PROTOCOL", view_protocol);
9562
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$OPT_TRACE_PROTOCOL", opt_trace_protocol);
9563
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$EXPLAIN_PROTOCOL", explain_protocol);
9564
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$JSON_EXPLAIN_PROTOCOL", json_explain_protocol);
9565
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$CURSOR_PROTOCOL", cursor_protocol);
9566
9567
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$ENABLE_QUERY_LOG", 1);
9568
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$ENABLE_ABORT_ON_ERROR", 1);
9569
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$ENABLE_RESULT_LOG", 1);
9570
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$ENABLE_CONNECT_LOG", 0);
9571
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$ENABLE_WARNINGS", 1);
9572
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$ENABLE_INFO", 0);
9573
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$ENABLE_METADATA", 0);
9574
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_int("$ENABLE_ASYNC_CLIENT", 0);
9575
9576
3/10
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 55506 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 55506 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
55506 DBUG_PRINT("info",
9577 ("result_file: '%s'", result_file_name ? result_file_name : ""));
9578
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 verbose_msg("Results saved in '%s'.",
9579
2/2
✓ Branch 0 taken 41273 times.
✓ Branch 1 taken 14233 times.
55506 result_file_name ? result_file_name : "");
9580
2/4
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 55506 times.
55506 if (mysql_server_init(0, nullptr, nullptr))
9581 die("Can't initialize MySQL server");
9582 55506 server_initialized = true;
9583
3/4
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 255 times.
✓ Branch 3 taken 55251 times.
55506 if (cur_file == file_stack && cur_file->file == nullptr) {
9584 255 cur_file->file = stdin;
9585 510 cur_file->file_name =
9586
1/2
✓ Branch 0 taken 255 times.
✗ Branch 1 not taken.
255 my_strdup(PSI_NOT_INSTRUMENTED, "<stdin>", MYF(MY_WME));
9587 255 cur_file->lineno = 1;
9588 }
9589
9590
2/4
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 55506 times.
55506 if (log_file.open(opt_logdir, result_file_name, ".log")) cleanup_and_exit(1);
9591
9592
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 verbose_msg("Logging to '%s'.", log_file.file_name());
9593 55506 enable_async_client = use_async_client;
9594
9595 // Creating a log file using current file name if result file doesn't exist.
9596
2/2
✓ Branch 0 taken 41273 times.
✓ Branch 1 taken 14233 times.
55506 if (result_file_name) {
9597
2/4
✓ Branch 0 taken 41273 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 41273 times.
41273 if (log_file.open(opt_logdir, result_file_name, ".log"))
9598 cleanup_and_exit(1);
9599 } else {
9600
2/2
✓ Branch 0 taken 13978 times.
✓ Branch 1 taken 255 times.
14233 if (std::strcmp(cur_file->file_name, "<stdin>")) {
9601
2/4
✓ Branch 0 taken 13978 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 13978 times.
13978 if (log_file.open(opt_logdir, cur_file->file_name, ".log"))
9602 cleanup_and_exit(1);
9603 } else {
9604
2/4
✓ Branch 0 taken 255 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 255 times.
255 if (log_file.open(opt_logdir, "stdin", ".log")) cleanup_and_exit(1);
9605 }
9606 }
9607
9608
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55506 times.
55506 if (opt_mark_progress) {
9609 if (result_file_name) {
9610 if (progress_file.open(opt_logdir, result_file_name, ".progress"))
9611 cleanup_and_exit(1);
9612 } else {
9613 if (std::strcmp(cur_file->file_name, "<stdin>")) {
9614 if (progress_file.open(opt_logdir, cur_file->file_name, ".progress"))
9615 cleanup_and_exit(1);
9616 } else {
9617 if (progress_file.open(opt_logdir, "stdin", ".progress"))
9618 cleanup_and_exit(1);
9619 }
9620 }
9621 verbose_msg("Tracing progress in '%s'.", progress_file.file_name());
9622 }
9623
9624
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 var_set_string("MYSQLTEST_FILE", cur_file->file_name);
9625
9626 /* Cursor protocol implies ps protocol */
9627
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55506 times.
55506 if (cursor_protocol) ps_protocol = true;
9628
9629 55506 ps_protocol_enabled = ps_protocol;
9630 55506 sp_protocol_enabled = sp_protocol;
9631 55506 view_protocol_enabled = view_protocol;
9632 55506 opt_trace_protocol_enabled = opt_trace_protocol;
9633 55506 explain_protocol_enabled = explain_protocol;
9634 55506 json_explain_protocol_enabled = json_explain_protocol;
9635 55506 cursor_protocol_enabled = cursor_protocol;
9636
9637 55506 st_connection *con = connections;
9638
2/4
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 55506 times.
55506 if (!(mysql_init(&con->mysql))) die("Failed in mysql_init()");
9639
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55506 times.
55506 if (opt_init_command)
9640 mysql_options(&con->mysql, MYSQL_INIT_COMMAND, opt_init_command);
9641
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 if (opt_connect_timeout)
9642
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 mysql_options(&con->mysql, MYSQL_OPT_CONNECT_TIMEOUT,
9643 (void *)&opt_connect_timeout);
9644
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 55505 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
55506 if (opt_compress) mysql_options(&con->mysql, MYSQL_OPT_COMPRESS, NullS);
9645
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 mysql_options(&con->mysql, MYSQL_OPT_LOCAL_INFILE, nullptr);
9646
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 55506 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 55506 times.
55506 if (0 != std::strcmp(default_charset, charset_info->csname) &&
9647 !(charset_info =
9648 get_charset_by_csname(default_charset, MY_CS_PRIMARY, MYF(MY_WME))))
9649 die("Invalid character set specified.");
9650
1/2
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
55506 mysql_options(&con->mysql, MYSQL_SET_CHARSET_NAME, charset_info->csname);
9651
2/2
✓ Branch 0 taken 11946 times.
✓ Branch 1 taken 43560 times.
55506 if (opt_charsets_dir)
9652
1/2
✓ Branch 0 taken 11946 times.
✗ Branch 1 not taken.
11946 mysql_options(&con->mysql, MYSQL_SET_CHARSET_DIR, opt_charsets_dir);
9653
9654
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 55504 times.
55506 if (opt_protocol)
9655
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 mysql_options(&con->mysql, MYSQL_OPT_PROTOCOL, (char *)&opt_protocol);
9656
9657 /* Turn on VERIFY_IDENTITY mode only if host=="localhost". */
9658
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55506 times.
55506 if (opt_ssl_mode == SSL_MODE_VERIFY_IDENTITY) {
9659 if (!opt_host || std::strcmp(opt_host, "localhost"))
9660 opt_ssl_mode = SSL_MODE_VERIFY_CA;
9661 }
9662
9663
2/4
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 55506 times.
55506 if (SSL_SET_OPTIONS(&con->mysql)) die("%s", SSL_SET_OPTIONS_ERROR);
9664 #if defined(_WIN32)
9665 if (shared_memory_base_name)
9666 mysql_options(&con->mysql, MYSQL_SHARED_MEMORY_BASE_NAME,
9667 shared_memory_base_name);
9668
9669 if (opt_ssl_mode == SSL_MODE_DISABLED)
9670 mysql_options(&con->mysql, MYSQL_OPT_PROTOCOL,
9671 (char *)&opt_protocol_for_default_connection);
9672 #endif
9673
9674
2/4
✓ Branch 0 taken 55506 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 55506 times.
55506 if (!(con->name = my_strdup(PSI_NOT_INSTRUMENTED, "default", MYF(MY_WME))))
9675 die("Out of memory");
9676
9677
1/2
✓ Branch 0 taken 55462 times.
✗ Branch 1 not taken.
55506 safe_connect(&con->mysql, con->name, opt_host, opt_user, opt_pass, opt_db,
9678 opt_port, unix_sock);
9679
9680
2/4
✓ Branch 0 taken 55460 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 55460 times.
55462 if (ssl_client_check_post_connect_ssl_setup(
9681 2 &con->mysql, [](const char *err) { die("%s", err); }))
9682 return 0;
9683
9684 /* Use all time until exit if no explicit 'start_timer' */
9685 55460 timer_start = timer_now();
9686
9687 /*
9688 Initialize $mysql_errno with -1, so we can
9689 - distinguish it from valid values ( >= 0 ) and
9690 - detect if there was never a command sent to the server
9691 */
9692
1/2
✓ Branch 0 taken 55460 times.
✗ Branch 1 not taken.
55460 var_set_errno(-1);
9693
9694
1/2
✓ Branch 0 taken 55460 times.
✗ Branch 1 not taken.
55460 set_current_connection(con);
9695
9696
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55460 times.
55460 if (opt_hypergraph) {
9697 int error = mysql_query_wrapper(
9698 &con->mysql, "SET optimizer_switch='hypergraph_optimizer=on';");
9699 if (error != 0) {
9700 die("--hypergraph was given, but the server does not support the "
9701 "hypergraph optimizer. (errno=%d)",
9702 my_errno());
9703 }
9704 }
9705
9706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55460 times.
55460 if (opt_include) {
9707 open_file(opt_include);
9708 }
9709
9710
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55460 times.
55460 if (opt_offload_count_file) {
9711 secondary_engine = new Secondary_engine();
9712 // Save the initial value of secondary engine execution status.
9713 if (secondary_engine->offload_count(&cur_con->mysql, "before"))
9714 cleanup_and_exit(1);
9715 }
9716
9717
1/2
✓ Branch 0 taken 55460 times.
✗ Branch 1 not taken.
55460 verbose_msg("Start processing test commands from '%s' ...",
9718 cur_file->file_name);
9719
7/8
✓ Branch 0 taken 2830857728 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2830817681 times.
✓ Branch 3 taken 40047 times.
✓ Branch 4 taken 2830817666 times.
✓ Branch 5 taken 15 times.
✓ Branch 6 taken 2830817666 times.
✓ Branch 7 taken 40062 times.
2830857733 while (!read_command(&command) && !abort_flag) {
9720 2830817666 int current_line_inc = 1, processed = 0;
9721
4/4
✓ Branch 0 taken 2731649311 times.
✓ Branch 1 taken 99168355 times.
✓ Branch 2 taken 108521399 times.
✓ Branch 3 taken 2623127912 times.
2830817666 if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND)
9722
1/2
✓ Branch 0 taken 207689753 times.
✗ Branch 1 not taken.
207689754 get_command_type(command);
9723
9724
6/6
✓ Branch 0 taken 4253988 times.
✓ Branch 1 taken 2826563677 times.
✓ Branch 2 taken 56 times.
✓ Branch 3 taken 4253932 times.
✓ Branch 4 taken 56 times.
✓ Branch 5 taken 2830817609 times.
2830817665 if (command->type == Q_ERROR && expected_errors->count())
9725 // Delete all the error codes from previous 'error' command.
9726 56 expected_errors->clear_list();
9727
9728
4/4
✓ Branch 0 taken 2384 times.
✓ Branch 1 taken 2830815281 times.
✓ Branch 2 taken 2283 times.
✓ Branch 3 taken 101 times.
2830817665 if (testcase_disabled && command->type != Q_ENABLE_TESTCASE &&
9729
2/2
✓ Branch 0 taken 2282 times.
✓ Branch 1 taken 1 times.
2283 command->type != Q_DISABLE_TESTCASE) {
9730 // Test case is disabled, silently convert this line to a comment
9731 2282 command->type = Q_COMMENT;
9732 }
9733
9734 /* (Re-)set abort_on_error for this command */
9735
4/4
✓ Branch 0 taken 2828931216 times.
✓ Branch 1 taken 1886449 times.
✓ Branch 2 taken 2823150842 times.
✓ Branch 3 taken 5780374 times.
2830817665 command->abort_on_error = (expected_errors->count() == 0 && abort_on_error);
9736
9737 /* delimiter needs to be executed so we can continue to parse */
9738
4/4
✓ Branch 0 taken 2596580423 times.
✓ Branch 1 taken 234237242 times.
✓ Branch 2 taken 56900 times.
✓ Branch 3 taken 2596523523 times.
2830817665 bool ok_to_do = cur_block->ok || command->type == Q_DELIMITER;
9739
9740 /*
9741 'source' command needs to be "done" the first time if it may get
9742 re-iterated over in a true context. This can only happen if there's
9743 a while loop at some level above the current block.
9744 */
9745
4/4
✓ Branch 0 taken 2596523523 times.
✓ Branch 1 taken 234294142 times.
✓ Branch 2 taken 24696819 times.
✓ Branch 3 taken 2571826704 times.
2830817665 if (!ok_to_do && command->type == Q_SOURCE) {
9746
2/2
✓ Branch 0 taken 93333961 times.
✓ Branch 1 taken 371673 times.
93705634 for (struct st_block *stb = cur_block - 1; stb >= block_stack; stb--) {
9747
2/2
✓ Branch 0 taken 24325146 times.
✓ Branch 1 taken 69008815 times.
93333961 if (stb->cmd == cmd_while) {
9748 24325146 ok_to_do = true;
9749 24325146 break;
9750 }
9751 }
9752 }
9753
9754 /*
9755 Some commands need to be parsed in false context also to
9756 avoid any parsing errors.
9757 */
9758
2/2
✓ Branch 0 taken 2572198377 times.
✓ Branch 1 taken 258619288 times.
2830817665 if (!ok_to_do &&
9759
4/4
✓ Branch 0 taken 2572188432 times.
✓ Branch 1 taken 9945 times.
✓ Branch 2 taken 2570093538 times.
✓ Branch 3 taken 2094894 times.
2572198377 (command->type == Q_APPEND_FILE || command->type == Q_PERL ||
9760
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 2570093430 times.
2570093538 command->type == Q_WRITE_FILE)) {
9761 2104947 ok_to_do = true;
9762 }
9763
9764
2/2
✓ Branch 0 taken 260724235 times.
✓ Branch 1 taken 2570093430 times.
2830817665 if (ok_to_do) {
9765 260724235 command->last_argument = command->first_argument;
9766 260724235 processed = 1;
9767 /* Need to remember this for handle_error() */
9768 260724235 curr_command = command;
9769
89/92
✓ Branch 0 taken 47129 times.
✓ Branch 1 taken 1120176 times.
✓ Branch 2 taken 32744 times.
✓ Branch 3 taken 535953 times.
✓ Branch 4 taken 472447 times.
✓ Branch 5 taken 553506 times.
✓ Branch 6 taken 11651 times.
✓ Branch 7 taken 603241 times.
✓ Branch 8 taken 86972 times.
✓ Branch 9 taken 519 times.
✓ Branch 10 taken 388 times.
✓ Branch 11 taken 34089 times.
✓ Branch 12 taken 41746 times.
✓ Branch 13 taken 2257 times.
✓ Branch 14 taken 2260 times.
✓ Branch 15 taken 22 times.
✓ Branch 16 taken 21 times.
✓ Branch 17 taken 197 times.
✓ Branch 18 taken 197 times.
✓ Branch 19 taken 26971212 times.
✓ Branch 20 taken 1780166 times.
✓ Branch 21 taken 208 times.
✓ Branch 22 taken 5519756 times.
✓ Branch 23 taken 4225934 times.
✓ Branch 24 taken 1997095 times.
✓ Branch 25 taken 122176 times.
✓ Branch 26 taken 652 times.
✓ Branch 27 taken 173 times.
✓ Branch 28 taken 360 times.
✓ Branch 29 taken 216 times.
✓ Branch 30 taken 722 times.
✓ Branch 31 taken 61 times.
✓ Branch 32 taken 765 times.
✓ Branch 33 taken 298 times.
✓ Branch 34 taken 147482 times.
✓ Branch 35 taken 805 times.
✓ Branch 36 taken 30751 times.
✓ Branch 37 taken 160651 times.
✓ Branch 38 taken 16881 times.
✓ Branch 39 taken 2000 times.
✓ Branch 40 taken 93 times.
✓ Branch 41 taken 13118 times.
✓ Branch 42 taken 4209 times.
✓ Branch 43 taken 263 times.
✓ Branch 44 taken 366 times.
✓ Branch 45 taken 2136573 times.
✗ Branch 46 not taken.
✓ Branch 47 taken 106098 times.
✓ Branch 48 taken 818 times.
✓ Branch 49 taken 821 times.
✓ Branch 50 taken 31644 times.
✓ Branch 51 taken 70 times.
✓ Branch 52 taken 20 times.
✓ Branch 53 taken 4012 times.
✓ Branch 54 taken 25335431 times.
✓ Branch 55 taken 67855 times.
✓ Branch 56 taken 6484678 times.
✓ Branch 57 taken 4016377 times.
✓ Branch 58 taken 108971 times.
✓ Branch 59 taken 1886027 times.
✓ Branch 60 taken 35292 times.
✓ Branch 61 taken 9822 times.
✓ Branch 62 taken 14127 times.
✓ Branch 63 taken 305 times.
✓ Branch 64 taken 251 times.
✓ Branch 65 taken 185 times.
✓ Branch 66 taken 1816 times.
✓ Branch 67 taken 116533236 times.
✓ Branch 68 taken 12803786 times.
✓ Branch 69 taken 8 times.
✓ Branch 70 taken 29 times.
✓ Branch 71 taken 10 times.
✓ Branch 72 taken 65 times.
✓ Branch 73 taken 5214 times.
✓ Branch 74 taken 27527 times.
✓ Branch 75 taken 10648 times.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✓ Branch 78 taken 271 times.
✓ Branch 79 taken 283 times.
✓ Branch 80 taken 255 times.
✓ Branch 81 taken 12792 times.
✓ Branch 82 taken 12994 times.
✓ Branch 83 taken 35 times.
✓ Branch 84 taken 35 times.
✓ Branch 85 taken 107 times.
✓ Branch 86 taken 102 times.
✓ Branch 87 taken 71 times.
✓ Branch 88 taken 27 times.
✓ Branch 89 taken 14995 times.
✓ Branch 90 taken 1649 times.
✓ Branch 91 taken 46516975 times.
260724235 switch (command->type) {
9770 47129 case Q_CONNECT:
9771
1/2
✓ Branch 0 taken 47119 times.
✗ Branch 1 not taken.
47129 do_connect(command);
9772 47119 break;
9773 1120176 case Q_CONNECTION:
9774
1/2
✓ Branch 0 taken 1120175 times.
✗ Branch 1 not taken.
1120176 select_connection(command);
9775 1120175 break;
9776 32744 case Q_DISCONNECT:
9777 case Q_DIRTY_CLOSE:
9778
1/2
✓ Branch 0 taken 32742 times.
✗ Branch 1 not taken.
32744 do_close_connection(command);
9779 32742 break;
9780 535953 case Q_ENABLE_QUERY_LOG:
9781
1/2
✓ Branch 0 taken 535953 times.
✗ Branch 1 not taken.
535953 set_property(command, P_QUERY, false);
9782 535953 break;
9783 472447 case Q_DISABLE_QUERY_LOG:
9784
1/2
✓ Branch 0 taken 472447 times.
✗ Branch 1 not taken.
472447 set_property(command, P_QUERY, true);
9785 472447 break;
9786 553506 case Q_ENABLE_ABORT_ON_ERROR:
9787
1/2
✓ Branch 0 taken 553506 times.
✗ Branch 1 not taken.
553506 set_property(command, P_ABORT, true);
9788 553506 break;
9789 11651 case Q_DISABLE_ABORT_ON_ERROR:
9790
1/2
✓ Branch 0 taken 11651 times.
✗ Branch 1 not taken.
11651 set_property(command, P_ABORT, false);
9791 11651 break;
9792 603241 case Q_ENABLE_RESULT_LOG:
9793
1/2
✓ Branch 0 taken 603241 times.
✗ Branch 1 not taken.
603241 set_property(command, P_RESULT, false);
9794 603241 break;
9795 86972 case Q_DISABLE_RESULT_LOG:
9796
1/2
✓ Branch 0 taken 86972 times.
✗ Branch 1 not taken.
86972 set_property(command, P_RESULT, true);
9797 86972 break;
9798 519 case Q_ENABLE_CONNECT_LOG:
9799
1/2
✓ Branch 0 taken 519 times.
✗ Branch 1 not taken.
519 set_property(command, P_CONNECT, false);
9800 519 break;
9801 388 case Q_DISABLE_CONNECT_LOG:
9802
1/2
✓ Branch 0 taken 388 times.
✗ Branch 1 not taken.
388 set_property(command, P_CONNECT, true);
9803 388 break;
9804 34089 case Q_ENABLE_WARNINGS:
9805
1/2
✓ Branch 0 taken 34082 times.
✗ Branch 1 not taken.
34089 do_enable_warnings(command);
9806 34082 break;
9807 41746 case Q_DISABLE_WARNINGS:
9808
1/2
✓ Branch 0 taken 41740 times.
✗ Branch 1 not taken.
41746 do_disable_warnings(command);
9809 41740 break;
9810 2257 case Q_ENABLE_INFO:
9811
1/2
✓ Branch 0 taken 2257 times.
✗ Branch 1 not taken.
2257 set_property(command, P_INFO, false);
9812 2257 break;
9813 2260 case Q_DISABLE_INFO:
9814
1/2
✓ Branch 0 taken 2260 times.
✗ Branch 1 not taken.
2260 set_property(command, P_INFO, true);
9815 2260 break;
9816 22 case Q_ENABLE_SESSION_TRACK_INFO:
9817
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 set_property(command, P_SESSION_TRACK, true);
9818 22 break;
9819 21 case Q_DISABLE_SESSION_TRACK_INFO:
9820
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 set_property(command, P_SESSION_TRACK, false);
9821 21 break;
9822 197 case Q_ENABLE_METADATA:
9823
1/2
✓ Branch 0 taken 197 times.
✗ Branch 1 not taken.
197 set_property(command, P_META, true);
9824 197 break;
9825 197 case Q_DISABLE_METADATA:
9826
1/2
✓ Branch 0 taken 197 times.
✗ Branch 1 not taken.
197 set_property(command, P_META, false);
9827 197 break;
9828 26971212 case Q_SOURCE:
9829
1/2
✓ Branch 0 taken 26971209 times.
✗ Branch 1 not taken.
26971212 do_source(command);
9830 26971209 break;
9831 1780166 case Q_SLEEP:
9832
1/2
✓ Branch 0 taken 1780159 times.
✗ Branch 1 not taken.
1780166 do_sleep(command);
9833 1780159 break;
9834 208 case Q_WAIT_FOR_SLAVE_TO_STOP:
9835
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
208 do_wait_for_slave_to_stop(command);
9836 208 break;
9837 5519756 case Q_INC:
9838
1/2
✓ Branch 0 taken 5519751 times.
✗ Branch 1 not taken.
5519756 do_modify_var(command, DO_INC);
9839 5519751 break;
9840 4225934 case Q_DEC:
9841
1/2
✓ Branch 0 taken 4225929 times.
✗ Branch 1 not taken.
4225934 do_modify_var(command, DO_DEC);
9842 4225929 break;
9843 1997095 case Q_ECHO:
9844
1/2
✓ Branch 0 taken 1997095 times.
✗ Branch 1 not taken.
1997095 do_echo(command);
9845 1997095 command_executed++;
9846 1997095 break;
9847 122176 case Q_REMOVE_FILE:
9848
1/2
✓ Branch 0 taken 122169 times.
✗ Branch 1 not taken.
122176 do_remove_file(command);
9849 122169 break;
9850 652 case Q_REMOVE_FILES_WILDCARD:
9851
1/2
✓ Branch 0 taken 645 times.
✗ Branch 1 not taken.
652 do_remove_files_wildcard(command);
9852 645 break;
9853 173 case Q_COPY_FILES_WILDCARD:
9854
1/2
✓ Branch 0 taken 167 times.
✗ Branch 1 not taken.
173 do_copy_files_wildcard(command);
9855 167 break;
9856 360 case Q_MKDIR:
9857
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 do_mkdir(command);
9858 360 break;
9859 216 case Q_RMDIR:
9860
1/2
✓ Branch 0 taken 216 times.
✗ Branch 1 not taken.
216 do_rmdir(command, false);
9861 216 break;
9862 722 case Q_FORCE_RMDIR:
9863
1/2
✓ Branch 0 taken 722 times.
✗ Branch 1 not taken.
722 do_rmdir(command, true);
9864 722 break;
9865 61 case Q_FORCE_CPDIR:
9866
1/2
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
61 do_force_cpdir(command);
9867 61 break;
9868 765 case Q_LIST_FILES:
9869
1/2
✓ Branch 0 taken 765 times.
✗ Branch 1 not taken.
765 do_list_files(command);
9870 765 break;
9871 298 case Q_LIST_FILES_WRITE_FILE:
9872
1/2
✓ Branch 0 taken 298 times.
✗ Branch 1 not taken.
298 do_list_files_write_file_command(command, false);
9873 298 break;
9874 147482 case Q_LIST_FILES_APPEND_FILE:
9875
1/2
✓ Branch 0 taken 147482 times.
✗ Branch 1 not taken.
147482 do_list_files_write_file_command(command, true);
9876 147482 break;
9877 805 case Q_FILE_EXIST:
9878
1/2
✓ Branch 0 taken 799 times.
✗ Branch 1 not taken.
805 do_file_exist(command);
9879 799 break;
9880 30751 case Q_WRITE_FILE:
9881
1/2
✓ Branch 0 taken 30748 times.
✗ Branch 1 not taken.
30751 do_write_file(command);
9882 30748 break;
9883 160651 case Q_APPEND_FILE:
9884
1/2
✓ Branch 0 taken 160651 times.
✗ Branch 1 not taken.
160651 do_append_file(command);
9885 160651 break;
9886 16881 case Q_DIFF_FILES:
9887
1/2
✓ Branch 0 taken 16881 times.
✗ Branch 1 not taken.
16881 do_diff_files(command);
9888 16881 break;
9889 2000 case Q_SEND_QUIT:
9890
1/2
✓ Branch 0 taken 2000 times.
✗ Branch 1 not taken.
2000 do_send_quit(command);
9891 2000 break;
9892 93 case Q_CHANGE_USER:
9893
1/2
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
93 do_change_user(command);
9894 93 break;
9895 13118 case Q_CAT_FILE:
9896
1/2
✓ Branch 0 taken 13117 times.
✗ Branch 1 not taken.
13118 do_cat_file(command);
9897 13117 break;
9898 4209 case Q_COPY_FILE:
9899
1/2
✓ Branch 0 taken 4202 times.
✗ Branch 1 not taken.
4209 do_copy_file(command);
9900 4202 break;
9901 263 case Q_MOVE_FILE:
9902
1/2
✓ Branch 0 taken 256 times.
✗ Branch 1 not taken.
263 do_move_file(command);
9903 256 break;
9904 366 case Q_CHMOD_FILE:
9905
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
366 do_chmod_file(command);
9906 360 break;
9907 2136573 case Q_PERL:
9908
1/2
✓ Branch 0 taken 2136566 times.
✗ Branch 1 not taken.
2136573 do_perl(command);
9909 2136566 break;
9910 case Q_RESULT_FORMAT_VERSION:
9911 do_result_format_version(command);
9912 break;
9913 106098 case Q_DELIMITER:
9914
1/2
✓ Branch 0 taken 106098 times.
✗ Branch 1 not taken.
106098 do_delimiter(command);
9915 106098 break;
9916 818 case Q_DISPLAY_VERTICAL_RESULTS:
9917 818 display_result_vertically = true;
9918 818 break;
9919 821 case Q_DISPLAY_HORIZONTAL_RESULTS:
9920 821 display_result_vertically = false;
9921 821 break;
9922 31644 case Q_SORTED_RESULT:
9923 /*
9924 Turn on sorting of result set, will be reset after next
9925 command
9926 */
9927 31644 display_result_sorted = true;
9928 31644 start_sort_column = 0;
9929 31644 break;
9930 70 case Q_PARTIALLY_SORTED_RESULT:
9931 /*
9932 Turn on sorting of result set, will be reset after next
9933 command
9934 */
9935 70 display_result_sorted = true;
9936 70 start_sort_column = atoi(command->first_argument);
9937 70 command->last_argument = command->end;
9938 70 break;
9939 20 case Q_LOWERCASE:
9940 /*
9941 Turn on lowercasing of result, will be reset after next
9942 command
9943 */
9944 20 display_result_lower = true;
9945 20 break;
9946 4012 case Q_SKIP_IF_HYPERGRAPH:
9947 /*
9948 Skip the next query if running with --hypergraph; will be reset
9949 after next command.
9950 */
9951 4012 skip_if_hypergraph = true;
9952 4012 break;
9953 25335431 case Q_LET:
9954
1/2
✓ Branch 0 taken 25335396 times.
✗ Branch 1 not taken.
25335431 do_let(command);
9955 25335396 break;
9956 67855 case Q_EXPR:
9957
1/2
✓ Branch 0 taken 67843 times.
✗ Branch 1 not taken.
67855 do_expr(command);
9958 67843 break;
9959 6484678 case Q_EVAL:
9960 case Q_QUERY_VERTICAL:
9961 case Q_QUERY_HORIZONTAL:
9962
2/2
✓ Branch 0 taken 260937 times.
✓ Branch 1 taken 6223741 times.
6484678 if (command->query == command->query_buf) {
9963 /* Skip the first part of command, i.e query_xxx */
9964 260937 command->query = command->first_argument;
9965 260937 command->first_word_len = 0;
9966 }
9967 [[fallthrough]];
9968 case Q_QUERY:
9969 case Q_REAP: {
9970 10501055 bool old_display_result_vertically = display_result_vertically;
9971 /* Default is full query, both reap and send */
9972 10501055 int flags = QUERY_REAP_FLAG | QUERY_SEND_FLAG;
9973
9974
2/2
✓ Branch 0 taken 93797 times.
✓ Branch 1 taken 10407258 times.
10501055 if (q_send_flag) {
9975 // Last command was an empty 'send' or 'send_eval'
9976 93797 flags = QUERY_SEND_FLAG;
9977
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 93793 times.
93797 if (q_send_flag == 2)
9978 // Last command was an empty 'send_eval' command. Set the command
9979 // type to Q_SEND_EVAL so that the variable gets replaced with its
9980 // value before executing.
9981 4 command->type = Q_SEND_EVAL;
9982 93797 q_send_flag = 0;
9983
2/2
✓ Branch 0 taken 106495 times.
✓ Branch 1 taken 10300763 times.
10407258 } else if (command->type == Q_REAP) {
9984 106495 flags = QUERY_REAP_FLAG;
9985 }
9986
9987 /* Check for special property for this query */
9988 10501055 display_result_vertically |= (command->type == Q_QUERY_VERTICAL);
9989
9990 /*
9991 We run EXPLAIN _before_ the query. If query is UPDATE/DELETE is
9992 matters: a DELETE may delete rows, and then EXPLAIN DELETE will
9993 usually terminate quickly with "no matching rows". To make it more
9994 interesting, EXPLAIN is now first.
9995 */
9996
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10501055 times.
10501055 if (explain_protocol_enabled)
9997 run_explain(cur_con, command, flags, false);
9998
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10501055 times.
10501055 if (json_explain_protocol_enabled)
9999 run_explain(cur_con, command, flags, true);
10000
10001
2/2
✓ Branch 0 taken 1649 times.
✓ Branch 1 taken 10499406 times.
10501055 if (*output_file) {
10002
1/2
✓ Branch 0 taken 1649 times.
✗ Branch 1 not taken.
1649 strmake(command->output_file, output_file, sizeof(output_file) - 1);
10003 1649 *output_file = 0;
10004 }
10005
1/2
✓ Branch 0 taken 10501027 times.
✗ Branch 1 not taken.
10501055 run_query(cur_con, command, flags);
10006
1/2
✓ Branch 0 taken 10501027 times.
✗ Branch 1 not taken.
10501027 display_opt_trace(cur_con, command, flags);
10007 10501027 command_executed++;
10008 10501027 command->last_argument = command->end;
10009
10010 /* Restore settings */
10011 10501027 display_result_vertically = old_display_result_vertically;
10012
10013 10501027 break;
10014 }
10015 108971 case Q_SEND:
10016 case Q_SEND_EVAL:
10017
2/2
✓ Branch 0 taken 93797 times.
✓ Branch 1 taken 15174 times.
108971 if (!*command->first_argument) {
10018 // This is a 'send' or 'send_eval' command without arguments, it
10019 // indicates that _next_ query should be send only.
10020
2/2
✓ Branch 0 taken 93793 times.
✓ Branch 1 taken 4 times.
93797 if (command->type == Q_SEND)
10021 93793 q_send_flag = 1;
10022
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 else if (command->type == Q_SEND_EVAL)
10023 4 q_send_flag = 2;
10024 93797 break;
10025 }
10026
10027 /* Remove "send" if this is first iteration */
10028
2/2
✓ Branch 0 taken 4309 times.
✓ Branch 1 taken 10865 times.
15174 if (command->query == command->query_buf)
10029 4309 command->query = command->first_argument;
10030
10031 /*
10032 run_query() can execute a query partially, depending on the flags.
10033 QUERY_SEND_FLAG flag without QUERY_REAP_FLAG tells it to just send
10034 the query and read the result some time later when reap instruction
10035 is given on this connection.
10036 */
10037
1/2
✓ Branch 0 taken 15174 times.
✗ Branch 1 not taken.
15174 run_query(cur_con, command, QUERY_SEND_FLAG);
10038 15174 command_executed++;
10039 15174 command->last_argument = command->end;
10040 15174 break;
10041 1886027 case Q_ERROR:
10042
1/2
✓ Branch 0 taken 1886014 times.
✗ Branch 1 not taken.
1886027 do_error(command);
10043 1886014 break;
10044 35292 case Q_REPLACE:
10045
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35292 do_get_replace(command);
10046 35286 break;
10047 9822 case Q_REPLACE_REGEX:
10048
1/2
✓ Branch 0 taken 9815 times.
✗ Branch 1 not taken.
9822 do_get_replace_regex(command);
10049 9815 break;
10050 14127 case Q_REPLACE_COLUMN:
10051
1/2
✓ Branch 0 taken 14122 times.
✗ Branch 1 not taken.
14127 do_get_replace_column(command);
10052 14122 break;
10053 305 case Q_REPLACE_NUMERIC_ROUND:
10054
1/2
✓ Branch 0 taken 302 times.
✗ Branch 1 not taken.
305 do_get_replace_numeric_round(command);
10055 302 break;
10056 251 case Q_SAVE_MASTER_POS:
10057
1/2
✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
251 do_save_master_pos();
10058 251 break;
10059 185 case Q_SYNC_WITH_MASTER:
10060
1/2
✓ Branch 0 taken 183 times.
✗ Branch 1 not taken.
185 do_sync_with_master(command);
10061 183 break;
10062 1816 case Q_SYNC_SLAVE_WITH_MASTER: {
10063
1/2
✓ Branch 0 taken 1816 times.
✗ Branch 1 not taken.
1816 do_save_master_pos();
10064
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 1780 times.
1816 if (*command->first_argument)
10065
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 select_connection(command);
10066 else
10067
1/2
✓ Branch 0 taken 1780 times.
✗ Branch 1 not taken.
1780 select_connection_name("slave");
10068
1/2
✓ Branch 0 taken 1814 times.
✗ Branch 1 not taken.
1816 do_sync_with_master2(command, 0);
10069 1814 break;
10070 }
10071 116533236 case Q_COMMENT: {
10072 116533236 command->last_argument = command->end;
10073
10074 /* Don't output comments in v1 */
10075
1/2
✓ Branch 0 taken 116533236 times.
✗ Branch 1 not taken.
116533236 if (opt_result_format_version == 1) break;
10076
10077 /* Don't output comments if query logging is off */
10078 if (disable_query_log) break;
10079
10080 /* Write comment's with two starting #'s to result file */
10081 const char *p = command->query;
10082 if (p && *p == '#' && *(p + 1) == '#') {
10083 dynstr_append_mem(&ds_res, command->query, command->query_len);
10084 dynstr_append(&ds_res, "\n");
10085 }
10086 break;
10087 }
10088 12803786 case Q_EMPTY_LINE:
10089 /* Don't output newline in v1 */
10090
1/2
✓ Branch 0 taken 12803786 times.
✗ Branch 1 not taken.
12803786 if (opt_result_format_version == 1) break;
10091
10092 /* Don't output newline if query logging is off */
10093 if (disable_query_log) break;
10094
10095 dynstr_append(&ds_res, "\n");
10096 break;
10097 8 case Q_PING:
10098
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 handle_command_error(command, mysql_ping(&cur_con->mysql));
10099 8 break;
10100 29 case Q_RESET_CONNECTION:
10101
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
29 do_reset_connection();
10102
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 global_attrs->clear();
10103 28 break;
10104 10 case Q_QUERY_ATTRIBUTES:
10105
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 do_query_attributes(command);
10106 10 break;
10107 65 case Q_SEND_SHUTDOWN:
10108
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65 times.
65 if (opt_offload_count_file) {
10109 // Save the value of secondary engine execution status
10110 // before shutting down the server.
10111 if (secondary_engine->offload_count(&cur_con->mysql, "after"))
10112 cleanup_and_exit(1);
10113 }
10114
10115 65 handle_command_error(
10116
2/4
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 65 times.
✗ Branch 3 not taken.
65 command, mysql_query_wrapper(&cur_con->mysql, "shutdown"));
10117 65 break;
10118 5214 case Q_SHUTDOWN_SERVER:
10119
1/2
✓ Branch 0 taken 5190 times.
✗ Branch 1 not taken.
5214 do_shutdown_server(command);
10120 5190 break;
10121 27527 case Q_EXEC:
10122 case Q_EXECW:
10123
1/2
✓ Branch 0 taken 27472 times.
✗ Branch 1 not taken.
27527 do_exec(command, false);
10124 27472 command_executed++;
10125 27472 break;
10126 10648 case Q_EXEC_BACKGROUND:
10127
1/2
✓ Branch 0 taken 10648 times.
✗ Branch 1 not taken.
10648 do_exec(command, true);
10128 10648 command_executed++;
10129 10648 break;
10130 case Q_START_TIMER:
10131 /* Overwrite possible earlier start of timer */
10132 timer_start = timer_now();
10133 break;
10134 case Q_END_TIMER:
10135 /* End timer before ending mysqltest */
10136 timer_output();
10137 break;
10138 271 case Q_CHARACTER_SET:
10139
1/2
✓ Branch 0 taken 271 times.
✗ Branch 1 not taken.
271 do_set_charset(command);
10140 271 break;
10141 283 case Q_DISABLE_PS_PROTOCOL:
10142
1/2
✓ Branch 0 taken 283 times.
✗ Branch 1 not taken.
283 set_property(command, P_PS, false);
10143 /* Close any open statements */
10144
1/2
✓ Branch 0 taken 283 times.
✗ Branch 1 not taken.
283 close_statements();
10145 283 break;
10146 255 case Q_ENABLE_PS_PROTOCOL:
10147
1/2
✓ Branch 0 taken 255 times.
✗ Branch 1 not taken.
255 set_property(command, P_PS, ps_protocol);
10148 255 break;
10149 12792 case Q_DISABLE_RECONNECT:
10150
1/2
✓ Branch 0 taken 12792 times.
✗ Branch 1 not taken.
12792 set_reconnect(&cur_con->mysql, 0);
10151 12792 break;
10152 12994 case Q_ENABLE_RECONNECT:
10153
1/2
✓ Branch 0 taken 12994 times.
✗ Branch 1 not taken.
12994 set_reconnect(&cur_con->mysql, 1);
10154 12994 enable_async_client = false;
10155 /* Close any open statements - no reconnect, need new prepare */
10156
1/2
✓ Branch 0 taken 12994 times.
✗ Branch 1 not taken.
12994 close_statements();
10157 12994 break;
10158 35 case Q_ENABLE_ASYNC_CLIENT:
10159
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35 set_property(command, P_ASYNC, true);
10160 35 break;
10161 35 case Q_DISABLE_ASYNC_CLIENT:
10162
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35 set_property(command, P_ASYNC, false);
10163 35 break;
10164 107 case Q_DISABLE_TESTCASE:
10165
2/2
✓ Branch 0 taken 106 times.
✓ Branch 1 taken 1 times.
107 if (testcase_disabled == 0)
10166
1/2
✓ Branch 0 taken 103 times.
✗ Branch 1 not taken.
106 do_disable_testcase(command);
10167 else
10168 1 die("Test case is already disabled.");
10169 103 break;
10170 102 case Q_ENABLE_TESTCASE:
10171 // Ensure we don't get testcase_disabled < 0 as this would
10172 // accidentally disable code we don't want to have disabled.
10173
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 1 times.
102 if (testcase_disabled == 1)
10174 101 testcase_disabled = false;
10175 else
10176 1 die("Test case is already enabled.");
10177 101 break;
10178 71 case Q_DIE:
10179 /* Abort test with error code and error message */
10180 71 die("%s", command->first_argument);
10181 break;
10182 27 case Q_EXIT:
10183 /* Stop processing any more commands */
10184 27 abort_flag = true;
10185 27 break;
10186 14995 case Q_SKIP: {
10187 DYNAMIC_STRING ds_skip_msg;
10188
1/2
✓ Branch 0 taken 14995 times.
✗ Branch 1 not taken.
14995 init_dynamic_string(&ds_skip_msg, nullptr, command->query_len);
10189
10190 // Evaluate the skip message
10191
1/2
✓ Branch 0 taken 14995 times.
✗ Branch 1 not taken.
14995 do_eval(&ds_skip_msg, command->first_argument, command->end, false);
10192
10193 char skip_msg[FN_REFLEN];
10194
1/2
✓ Branch 0 taken 14995 times.
✗ Branch 1 not taken.
14995 if (ds_skip_msg.length > 0) {
10195
1/2
✓ Branch 0 taken 14995 times.
✗ Branch 1 not taken.
14995 strmake(skip_msg, ds_skip_msg.str, FN_REFLEN - 1);
10196 } else {
10197 skip_msg[0] = '\0';
10198 }
10199
10200
1/2
✓ Branch 0 taken 14995 times.
✗ Branch 1 not taken.
14995 dynstr_free(&ds_skip_msg);
10201
10202
2/2
✓ Branch 0 taken 14994 times.
✓ Branch 1 taken 1 times.
14995 if (!no_skip) {
10203 // --no-skip option is disabled, skip the test case
10204 14994 abort_not_supported_test("%s", skip_msg);
10205 } else {
10206 1 const char *path = cur_file->file_name;
10207 1 const char *fn = get_filename_from_path(path);
10208
10209 // Check if the file is in excluded list
10210
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (excluded_string && strstr(excluded_string, fn)) {
10211 // File is present in excluded list, skip the test case
10212 abort_not_supported_test("%s", skip_msg);
10213 } else {
10214 // File is not present in excluded list, ignore the skip
10215 // and continue running the test case
10216 1 command->last_argument = command->end;
10217 1 skip_ignored = true; // Mark as noskip pass or fail.
10218 }
10219 }
10220 1 } break;
10221 1649 case Q_OUTPUT: {
10222 static DYNAMIC_STRING ds_to_file;
10223 1649 const struct command_arg output_file_args[] = {
10224 {"to_file", ARG_STRING, true, &ds_to_file, "Output filename"}};
10225
1/2
✓ Branch 0 taken 1649 times.
✗ Branch 1 not taken.
1649 check_command_args(command, command->first_argument, output_file_args,
10226 1, ' ');
10227
1/2
✓ Branch 0 taken 1649 times.
✗ Branch 1 not taken.
1649 strmake(output_file, ds_to_file.str, FN_REFLEN);
10228
1/2
✓ Branch 0 taken 1649 times.
✗ Branch 1 not taken.
1649 dynstr_free(&ds_to_file);
10229 1649 break;
10230 }
10231
10232 46516975 default:
10233 46516975 processed = 0;
10234 46516975 break;
10235 }
10236 }
10237
10238
2/2
✓ Branch 0 taken 2616610405 times.
✓ Branch 1 taken 214191894 times.
2830802299 if (!processed) {
10239 2616610405 current_line_inc = 0;
10240
5/5
✓ Branch 0 taken 17924620 times.
✓ Branch 1 taken 263961591 times.
✓ Branch 2 taken 42 times.
✓ Branch 3 taken 281870912 times.
✓ Branch 4 taken 2052853240 times.
2616610405 switch (command->type) {
10241 17924620 case Q_WHILE:
10242
1/2
✓ Branch 0 taken 17924615 times.
✗ Branch 1 not taken.
17924620 do_block(cmd_while, command);
10243 17924615 break;
10244 263961591 case Q_IF:
10245
1/2
✓ Branch 0 taken 263961585 times.
✗ Branch 1 not taken.
263961591 do_block(cmd_if, command);
10246 263961585 break;
10247 42 case Q_ASSERT:
10248
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
42 do_block(cmd_assert, command);
10249 34 break;
10250 281870912 case Q_END_BLOCK:
10251
1/2
✓ Branch 0 taken 281870909 times.
✗ Branch 1 not taken.
281870912 do_done(command);
10252 281870909 break;
10253 2052853240 default:
10254 2052853240 current_line_inc = 1;
10255 2052853240 break;
10256 }
10257 } else
10258
1/2
✓ Branch 0 taken 214191890 times.
✗ Branch 1 not taken.
214191894 check_eol_junk(command->last_argument);
10259
10260
4/4
✓ Branch 0 taken 2826548423 times.
✓ Branch 1 taken 4253850 times.
✓ Branch 2 taken 1569707008 times.
✓ Branch 3 taken 1256841415 times.
2830802273 if (command->type != Q_ERROR && command->type != Q_COMMENT &&
10261
4/4
✓ Branch 0 taken 1305745423 times.
✓ Branch 1 taken 263961585 times.
✓ Branch 2 taken 1023874514 times.
✓ Branch 3 taken 281870909 times.
1569707008 command->type != Q_IF && command->type != Q_END_BLOCK) {
10262 // As soon as any non "error" command or comment has been executed,
10263 // the array with expected errors should be cleared
10264 1023874514 expected_errors->clear_list();
10265 }
10266
10267
4/4
✓ Branch 0 taken 2818250857 times.
✓ Branch 1 taken 12551416 times.
✓ Branch 2 taken 167426 times.
✓ Branch 3 taken 2818083431 times.
2830802273 if (command_executed != last_command_executed || command->used_replace) {
10268 /*
10269 As soon as any command has been executed,
10270 the replace structures should be cleared
10271 */
10272
1/2
✓ Branch 0 taken 12718842 times.
✗ Branch 1 not taken.
12718842 free_all_replace();
10273
10274 /* Also reset "sorted_result", "lowercase" and "skip_if_hypergraph"*/
10275 12718842 display_result_sorted = false;
10276 12718842 display_result_lower = false;
10277 12718842 skip_if_hypergraph = false;
10278 }
10279 2830802273 last_command_executed = command_executed;
10280
10281 2830802273 parser.current_line += current_line_inc;
10282
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2830802273 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2830802273 if (opt_mark_progress) mark_progress(&progress_file, parser.current_line);
10283
10284 // Write result from command to log file immediately.
10285
1/2
✓ Branch 0 taken 2830802273 times.
✗ Branch 1 not taken.
2830802273 flush_ds_res();
10286 }
10287
10288 40062 start_lineno = 0;
10289
1/2
✓ Branch 0 taken 40062 times.
✗ Branch 1 not taken.
40062 verbose_msg("... Done processing test commands.");
10290
10291
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 40061 times.
40062 if (testcase_disabled) die("Test ended with test case execution disabled.");
10292
10293
5/6
✓ Branch 0 taken 40058 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 40058 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 40058 times.
40061 if (disable_warnings || disabled_warnings->count())
10294 3 die("The test didn't enable all the disabled warnings, enable "
10295 "all of them before end of the test.");
10296
10297 40058 bool empty_result = false;
10298
10299 /*
10300 The whole test has been executed _sucessfully_.
10301 Time to compare result or save it to record file.
10302 The entire output from test is in the log file
10303 */
10304
2/2
✓ Branch 0 taken 40056 times.
✓ Branch 1 taken 2 times.
40058 if (log_file.bytes_written()) {
10305
2/2
✓ Branch 0 taken 40034 times.
✓ Branch 1 taken 22 times.
40056 if (result_file_name) {
10306 /* A result file has been specified */
10307
10308
2/2
✓ Branch 0 taken 15500 times.
✓ Branch 1 taken 24534 times.
40034 if (record) {
10309 /* Recording */
10310
10311 /* save a copy of the log to result file */
10312
2/4
✓ Branch 0 taken 15500 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15500 times.
15500 if (my_copy(log_file.file_name(), result_file_name, MYF(0)) != 0)
10313 die("Failed to copy '%s' to '%s', errno: %d", log_file.file_name(),
10314 result_file_name, errno);
10315
10316 } else {
10317 /* Check that the output from test is equal to result file */
10318
1/2
✓ Branch 0 taken 24519 times.
✗ Branch 1 not taken.
24534 check_result();
10319 }
10320 }
10321 } else {
10322 /* Empty output is an error *unless* we also have an empty result file */
10323
5/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
3 if (!result_file_name || record ||
10324
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 compare_files(log_file.file_name(), result_file_name)) {
10325 1 die("The test didn't produce any output");
10326 } else {
10327 1 empty_result = true; /* Meaning empty was expected */
10328 }
10329 }
10330
10331
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 40041 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
40042 if (!command_executed && result_file_name && !empty_result)
10332 die("No queries executed but non-empty result file found!");
10333
10334
1/2
✓ Branch 0 taken 40042 times.
✗ Branch 1 not taken.
40042 verbose_msg("Test has succeeded!");
10335
1/2
✓ Branch 0 taken 40042 times.
✗ Branch 1 not taken.
40042 timer_output();
10336 /* Yes, if we got this far the test has succeeded! Sakila smiles */
10337 40042 cleanup_and_exit(0);
10338 return 0; /* Keep compiler happy too */
10339 }
10340
10341 /*
10342 A primitive timer that give results in milliseconds if the
10343 --timer-file=<filename> is given. The timer result is written
10344 to that file when the result is available. To not confuse
10345 mysql-test-run with an old obsolete result, we remove the file
10346 before executing any commands. The time we measure is
10347
10348 - If no explicit 'start_timer' or 'end_timer' is given in the
10349 test case, the timer measure how long we execute in mysqltest.
10350
10351 - If only 'start_timer' is given we measure how long we execute
10352 from that point until we terminate mysqltest.
10353
10354 - If only 'end_timer' is given we measure how long we execute
10355 from that we enter mysqltest to the 'end_timer' is command is
10356 executed.
10357
10358 - If both 'start_timer' and 'end_timer' are given we measure
10359 the time between executing the two commands.
10360 */
10361
10362 40042 void timer_output(void) {
10363
2/2
✓ Branch 0 taken 10436 times.
✓ Branch 1 taken 29606 times.
40042 if (timer_file) {
10364 char buf[32], *end;
10365 10436 ulonglong timer = timer_now() - timer_start;
10366
1/2
✓ Branch 0 taken 10436 times.
✗ Branch 1 not taken.
10436 end = longlong10_to_str(timer, buf, 10);
10367
1/2
✓ Branch 0 taken 10436 times.
✗ Branch 1 not taken.
10436 str_to_file(timer_file, buf, (int)(end - buf));
10368 /* Timer has been written to the file, don't use it anymore */
10369 10436 timer_file = nullptr;
10370 }
10371 40042 }
10372
10373 65896 ulonglong timer_now(void) { return my_micro_time() / 1000; }
10374
10375 /*
10376 Get arguments for replace_columns. The syntax is:
10377 replace-column column_number to_string [column_number to_string ...]
10378 Where each argument may be quoted with ' or "
10379 A argument may also be a variable, in which case the value of the
10380 variable is replaced.
10381 */
10382
10383 14127 void do_get_replace_column(struct st_command *command) {
10384 14127 const char *from = command->first_argument;
10385 char *buff, *start;
10386
1/2
✓ Branch 0 taken 14127 times.
✗ Branch 1 not taken.
14127 DBUG_TRACE;
10387
10388
1/2
✓ Branch 0 taken 14127 times.
✗ Branch 1 not taken.
14127 free_replace_column();
10389
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14127 times.
14127 if (!*from) die("Missing argument in %s", command->query);
10390
10391 /* Allocate a buffer for results */
10392
1/2
✓ Branch 0 taken 14127 times.
✗ Branch 1 not taken.
14127 start = buff = (char *)my_malloc(PSI_NOT_INSTRUMENTED, std::strlen(from) + 1,
10393 MYF(MY_WME | MY_FAE));
10394
2/2
✓ Branch 0 taken 143685 times.
✓ Branch 1 taken 14122 times.
157807 while (*from) {
10395 char *to;
10396 uint column_number;
10397
1/2
✓ Branch 0 taken 143685 times.
✗ Branch 1 not taken.
143685 to = get_string(&buff, &from, command);
10398
5/6
✓ Branch 0 taken 143681 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 143681 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 143681 times.
143685 if (!(column_number = atoi(to)) || column_number > MAX_COLUMNS)
10399 4 die("Wrong column number to replace_column in '%s'", command->query);
10400
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 143680 times.
143681 if (!*from)
10401 1 die("Wrong number of arguments to replace_column in '%s'",
10402 command->query);
10403
1/2
✓ Branch 0 taken 143680 times.
✗ Branch 1 not taken.
143680 to = get_string(&buff, &from, command);
10404
1/2
✓ Branch 0 taken 143680 times.
✗ Branch 1 not taken.
143680 my_free(replace_column[column_number - 1]);
10405 287360 replace_column[column_number - 1] =
10406
1/2
✓ Branch 0 taken 143680 times.
✗ Branch 1 not taken.
143680 my_strdup(PSI_NOT_INSTRUMENTED, to, MYF(MY_WME | MY_FAE));
10407 143680 max_replace_column = std::max(max_replace_column, column_number);
10408 }
10409
1/2
✓ Branch 0 taken 14122 times.
✗ Branch 1 not taken.
14122 my_free(start);
10410 14122 command->last_argument = command->end;
10411 14122 }
10412
10413 12788481 void free_replace_column() {
10414 uint i;
10415
2/2
✓ Branch 0 taken 321962 times.
✓ Branch 1 taken 12788481 times.
13110443 for (i = 0; i < max_replace_column; i++) {
10416
2/2
✓ Branch 0 taken 143676 times.
✓ Branch 1 taken 178286 times.
321962 if (replace_column[i]) {
10417 143676 my_free(replace_column[i]);
10418 143676 replace_column[i] = nullptr;
10419 }
10420 }
10421 12788481 max_replace_column = 0;
10422 12788481 }
10423
10424 /*
10425 Functions to round numeric results.
10426
10427 SYNOPSIS
10428 do_get_replace_numeric_round()
10429 command - command handle
10430
10431 DESCRIPTION
10432 replace_numeric_round <precision>
10433
10434 where precision is the number of digits after the decimal point
10435 that the result will be rounded off to. The precision can only
10436 be a number between 0 and 16.
10437 eg. replace_numeric_round 10;
10438 Numbers which are > 1e10 or < -1e10 are represented using the
10439 exponential notation after they are rounded off.
10440 Trailing zeroes after the decimal point are removed from the
10441 numbers.
10442 If the precision is 0, then the value is rounded off to the
10443 nearest whole number.
10444 */
10445 305 void do_get_replace_numeric_round(struct st_command *command) {
10446 DYNAMIC_STRING ds_round;
10447 305 const struct command_arg numeric_arg = {
10448 305 "precision", ARG_STRING, true, &ds_round, "Number of decimal precision"};
10449
1/2
✓ Branch 0 taken 305 times.
✗ Branch 1 not taken.
305 DBUG_TRACE;
10450
10451
1/2
✓ Branch 0 taken 304 times.
✗ Branch 1 not taken.
305 check_command_args(command, command->first_argument, &numeric_arg,
10452 sizeof(numeric_arg) / sizeof(struct command_arg), ' ');
10453
10454 // Parse the argument string to get the precision
10455 304 long int v = 0;
10456
3/4
✓ Branch 0 taken 304 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 302 times.
304 if (str2int(ds_round.str, 10, 0, REPLACE_ROUND_MAX, &v) == NullS)
10457 2 die("A number between 0 and %d is required for the precision "
10458 "in replace_numeric_round",
10459 REPLACE_ROUND_MAX);
10460
10461 302 glob_replace_numeric_round = (int)v;
10462
1/2
✓ Branch 0 taken 302 times.
✗ Branch 1 not taken.
302 dynstr_free(&ds_round);
10463 302 }
10464
10465 12774354 void free_replace_numeric_round() { glob_replace_numeric_round = -1; }
10466
10467 /*
10468 Round the digits after the decimal point to the specified precision
10469 by iterating through the result set element, identifying the part to
10470 be rounded off, and rounding that part off.
10471 */
10472 3675 void replace_numeric_round_append(int round, DYNAMIC_STRING *result,
10473 const char *from, size_t len) {
10474
2/2
✓ Branch 0 taken 26853 times.
✓ Branch 1 taken 361 times.
27214 while (len > 0) {
10475 // Move pointer to the start of the numeric values
10476 26853 size_t size = strcspn(from, "0123456789");
10477
2/2
✓ Branch 0 taken 24866 times.
✓ Branch 1 taken 1987 times.
26853 if (size > 0) {
10478 24866 dynstr_append_mem(result, from, size);
10479 24866 from += size;
10480 24866 len -= size;
10481 }
10482
10483 /*
10484 Move the pointer to the end of the numeric values and the
10485 the start of the non-numeric values such as "." and "e"
10486 */
10487 26853 size = strspn(from, "0123456789");
10488 26853 int r = round;
10489
10490 /*
10491 If result from one of the rows of the result set is null,
10492 break the loop
10493 */
10494
2/2
✓ Branch 0 taken 3314 times.
✓ Branch 1 taken 23539 times.
26853 if (*(from + size) == 0) {
10495 3314 dynstr_append_mem(result, from, size);
10496 3314 break;
10497 }
10498
10499
2/3
✓ Branch 0 taken 12567 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10972 times.
23539 switch (*(from + size)) {
10500 // double/float
10501 12567 case '.':
10502 size_t size1;
10503 12567 size1 = strspn(from + size + 1, "0123456789");
10504
10505 /*
10506 Restrict rounding to less than the
10507 the existing precision to avoid 1.2 being replaced
10508 to 1.2000000
10509 */
10510
2/2
✓ Branch 0 taken 344 times.
✓ Branch 1 taken 12223 times.
12567 if (size1 < (size_t)r) r = size1;
10511 // fallthrough: all cases till next break are executed
10512 [[fallthrough]];
10513 case 'e':
10514 case 'E':
10515
2/2
✓ Branch 0 taken 12493 times.
✓ Branch 1 taken 74 times.
12567 if (isdigit(*(from + size + 1))) {
10516 char *end;
10517 12493 double val = strtod(from, &end);
10518
1/2
✓ Branch 0 taken 12493 times.
✗ Branch 1 not taken.
12493 if (end != nullptr) {
10519
3/4
✓ Branch 0 taken 12492 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 12492 times.
✗ Branch 3 not taken.
12493 const char *format = (val < 1e10 && val > -1e10) ? "%.*f" : "%.*e";
10520 char buf[40];
10521
10522 12493 size = snprintf(buf, sizeof(buf), format, r, val);
10523
5/6
✓ Branch 0 taken 12492 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 12492 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11382 times.
✓ Branch 5 taken 1110 times.
12493 if (val < 1e10 && val > -1e10 && r > 0) {
10524 /*
10525 2.0000000 need to be represented as 2 for consistency
10526 2.0010000 also becomes 2.001
10527 */
10528
2/2
✓ Branch 0 taken 2617 times.
✓ Branch 1 taken 11382 times.
13999 while (buf[size - 1] == '0') size--;
10529
10530 // don't leave 100. trailing
10531
2/2
✓ Branch 0 taken 449 times.
✓ Branch 1 taken 10933 times.
11382 if (buf[size - 1] == '.') size--;
10532 }
10533
1/2
✓ Branch 0 taken 12493 times.
✗ Branch 1 not taken.
12493 dynstr_append_mem(result, buf, size);
10534 12493 len -= (end - from);
10535 12493 from = end;
10536 12493 break;
10537 }
10538 }
10539
10540 /*
10541 This is because strtod didn't convert or there wasn't digits after
10542 [.eE] so output without changing
10543 */
10544 74 dynstr_append_mem(result, from, size);
10545 74 from += size;
10546 74 len -= size;
10547 74 break;
10548 // int
10549 10972 default:
10550 10972 dynstr_append_mem(result, from, size);
10551 10972 from += size;
10552 10972 len -= size;
10553 10972 break;
10554 }
10555 }
10556 3675 }
10557
10558 /****************************************************************************/
10559 /*
10560 Replace functions
10561 */
10562
10563 /* Definitions for replace result */
10564
10565 struct POINTER_ARRAY { /* when using array-strings */
10566 TYPELIB typelib; /* Pointer to strings */
10567 uchar *str{nullptr}; /* Strings is here */
10568 uint8 *flag{nullptr}; /* Flag about each var. */
10569 uint array_allocs{0}, max_count{0}, length{0}, max_length{0};
10570 };
10571
10572 REPLACE *init_replace(const char **from, const char **to, uint count,
10573 const char *word_end_chars);
10574 int insert_pointer_name(POINTER_ARRAY *pa, char *name);
10575 void free_pointer_array(POINTER_ARRAY *pa);
10576
10577 /*
10578 Get arguments for replace. The syntax is:
10579 replace from to [from to ...]
10580 Where each argument may be quoted with ' or "
10581 A argument may also be a variable, in which case the value of the
10582 variable is replaced.
10583 */
10584
10585 35292 void do_get_replace(struct st_command *command) {
10586 uint i;
10587 35292 const char *from = command->first_argument;
10588 char *buff, *start;
10589 char word_end_chars[256], *pos;
10590 35292 POINTER_ARRAY to_array, from_array;
10591
1/2
✓ Branch 0 taken 35292 times.
✗ Branch 1 not taken.
35292 DBUG_TRACE;
10592
10593
1/2
✓ Branch 0 taken 35292 times.
✗ Branch 1 not taken.
35292 free_replace();
10594
10595
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35292 times.
35292 if (!*from) die("Missing argument in %s", command->query);
10596
1/2
✓ Branch 0 taken 35292 times.
✗ Branch 1 not taken.
35292 start = buff = (char *)my_malloc(PSI_NOT_INSTRUMENTED, std::strlen(from) + 1,
10597 MYF(MY_WME | MY_FAE));
10598
2/2
✓ Branch 0 taken 56054 times.
✓ Branch 1 taken 35286 times.
91340 while (*from) {
10599 56054 char *to = buff;
10600
1/2
✓ Branch 0 taken 56054 times.
✗ Branch 1 not taken.
56054 to = get_string(&buff, &from, command);
10601
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 56048 times.
56054 if (!*from)
10602 6 die("Wrong number of arguments to replace_result in '%s'",
10603 command->query);
10604 #ifdef _WIN32
10605 fix_win_paths(to, from - to);
10606 #endif
10607
1/2
✓ Branch 0 taken 56048 times.
✗ Branch 1 not taken.
56048 insert_pointer_name(&from_array, to);
10608
1/2
✓ Branch 0 taken 56048 times.
✗ Branch 1 not taken.
56048 to = get_string(&buff, &from, command);
10609
1/2
✓ Branch 0 taken 56048 times.
✗ Branch 1 not taken.
56048 insert_pointer_name(&to_array, to);
10610 }
10611
2/2
✓ Branch 0 taken 8997930 times.
✓ Branch 1 taken 35286 times.
9033216 for (i = 1, pos = word_end_chars; i < 256; i++)
10612
2/2
✓ Branch 0 taken 211781 times.
✓ Branch 1 taken 8786149 times.
8997930 if (my_isspace(charset_info, i)) *pos++ = i;
10613 35286 *pos = 0; /* End pointer */
10614
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35286 times.
35286 if (!(glob_replace = init_replace(
10615 from_array.typelib.type_names, to_array.typelib.type_names,
10616
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 (uint)from_array.typelib.count, word_end_chars)))
10617 die("Can't initialize replace from '%s'", command->query);
10618
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 free_pointer_array(&from_array);
10619
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 free_pointer_array(&to_array);
10620
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 my_free(start);
10621 35286 command->last_argument = command->end;
10622 35286 }
10623
10624 12809646 void free_replace() {
10625
1/2
✓ Branch 0 taken 12809646 times.
✗ Branch 1 not taken.
12809646 DBUG_TRACE;
10626
1/2
✓ Branch 0 taken 12809646 times.
✗ Branch 1 not taken.
12809646 my_free(glob_replace);
10627 12809646 glob_replace = nullptr;
10628 12809646 }
10629
10630 struct REPLACE {
10631 int found;
10632 REPLACE *next[256];
10633 };
10634
10635 struct REPLACE_STRING {
10636 int found;
10637 const char *replace_string;
10638 uint to_offset;
10639 int from_offset;
10640 };
10641
10642 309743 void replace_strings_append(REPLACE *rep, DYNAMIC_STRING *ds, const char *str,
10643 size_t len [[maybe_unused]]) {
10644 REPLACE *rep_pos;
10645 REPLACE_STRING *rep_str;
10646 const char *start, *from;
10647
1/2
✓ Branch 0 taken 309743 times.
✗ Branch 1 not taken.
309743 DBUG_TRACE;
10648
10649 309743 start = from = str;
10650 309743 rep_pos = rep + 1;
10651 for (;;) {
10652 /* Loop through states */
10653
3/8
✓ Branch 0 taken 345201 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 345201 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 345201 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
345201 DBUG_PRINT("info", ("Looping through states"));
10654
2/2
✓ Branch 0 taken 10832633 times.
✓ Branch 1 taken 345201 times.
11177834 while (!rep_pos->found) rep_pos = rep_pos->next[(uchar)*from++];
10655
10656 /* Does this state contain a string to be replaced */
10657
2/2
✓ Branch 0 taken 305895 times.
✓ Branch 1 taken 39306 times.
345201 if (!(rep_str = ((REPLACE_STRING *)rep_pos))->replace_string) {
10658 /* No match found */
10659
1/2
✓ Branch 0 taken 305895 times.
✗ Branch 1 not taken.
305895 dynstr_append_mem(ds, start, from - start - 1);
10660
3/8
✓ Branch 0 taken 305895 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 305895 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 305895 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
305895 DBUG_PRINT("exit",
10661 ("Found no more string to replace, appended: %s", start));
10662 305895 return;
10663 }
10664
10665 /* Found a string that needs to be replaced */
10666
3/8
✓ Branch 0 taken 39306 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39306 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 39306 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
39306 DBUG_PRINT("info", ("found: %d, to_offset: %d, from_offset: %d, string: %s",
10667 rep_str->found, rep_str->to_offset,
10668 rep_str->from_offset, rep_str->replace_string));
10669
10670 /* Append part of original string before replace string */
10671
1/2
✓ Branch 0 taken 39306 times.
✗ Branch 1 not taken.
39306 dynstr_append_mem(ds, start, (from - rep_str->to_offset) - start);
10672
10673 /* Append replace string */
10674
1/2
✓ Branch 0 taken 39306 times.
✗ Branch 1 not taken.
39306 dynstr_append_mem(ds, rep_str->replace_string,
10675 std::strlen(rep_str->replace_string));
10676
10677
5/6
✓ Branch 0 taken 3848 times.
✓ Branch 1 taken 35458 times.
✓ Branch 2 taken 3848 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3848 times.
✓ Branch 5 taken 35458 times.
39306 if (!*(from -= rep_str->from_offset) && rep_pos->found != 2) {
10678 /* End of from string */
10679
3/8
✓ Branch 0 taken 3848 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3848 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3848 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
3848 DBUG_PRINT("exit", ("Found end of from string"));
10680 3848 return;
10681 }
10682
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35458 times.
35458 assert(from <= str + len);
10683 35458 start = from;
10684 35458 rep_pos = rep;
10685 }
10686 309743 }
10687
10688 /*
10689 Regex replace functions
10690 */
10691
10692 /*
10693 Finds the next (non-escaped) '/' in the expression.
10694 (If the character '/' is needed, it can be escaped using '\'.)
10695 */
10696
10697 #define PARSE_REGEX_ARG \
10698 while (p < expr_end) { \
10699 char c = *p; \
10700 if (c == '/') { \
10701 if (last_c == '\\') { \
10702 buf_p[-1] = '/'; \
10703 } else { \
10704 *buf_p++ = 0; \
10705 break; \
10706 } \
10707 } else \
10708 *buf_p++ = c; \
10709 \
10710 last_c = c; \
10711 p++; \
10712 }
10713
10714 /**
10715 Initializes the regular substitution expression to be used in the
10716 result output of test.
10717
10718 @param expr Pointer to string having regular expression to be used
10719 for substitution.
10720 @retval st_replace_regex structure with pairs of substitutions.
10721 */
10722 9635 static struct st_replace_regex *init_replace_regex(const char *expr) {
10723 struct st_replace_regex *res;
10724 char *buf;
10725 const char *expr_end;
10726 const char *p;
10727 char *buf_p;
10728 9635 size_t expr_len = std::strlen(expr);
10729 9635 char last_c = 0;
10730 struct st_regex reg;
10731
10732 /* my_malloc() will die on fail with MY_FAE */
10733
1/2
✓ Branch 0 taken 9635 times.
✗ Branch 1 not taken.
9635 void *rawmem = my_malloc(PSI_NOT_INSTRUMENTED, sizeof(*res) + expr_len,
10734 MYF(MY_FAE + MY_WME));
10735
1/2
✓ Branch 0 taken 9635 times.
✗ Branch 1 not taken.
9635 res = new (rawmem) st_replace_regex;
10736
10737 9635 buf = (char *)res + sizeof(*res);
10738 9635 expr_end = expr + expr_len;
10739 9635 p = expr;
10740 9635 buf_p = buf;
10741
10742 /* for each regexp substitution statement */
10743
2/2
✓ Branch 0 taken 19914 times.
✓ Branch 1 taken 8593 times.
28507 while (p < expr_end) {
10744 19914 memset(&reg, 0, sizeof(reg));
10745 /* find the start of the statement */
10746
2/2
✓ Branch 0 taken 45953 times.
✓ Branch 1 taken 1040 times.
46993 while (p < expr_end) {
10747
2/2
✓ Branch 0 taken 18874 times.
✓ Branch 1 taken 27079 times.
45953 if (*p == '/') break;
10748 27079 p++;
10749 }
10750
10751
5/6
✓ Branch 0 taken 18874 times.
✓ Branch 1 taken 1040 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18874 times.
✓ Branch 4 taken 1040 times.
✓ Branch 5 taken 18874 times.
19914 if (p == expr_end || ++p == expr_end) {
10752
3/4
✓ Branch 0 taken 1040 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1035 times.
✓ Branch 3 taken 5 times.
1040 if (res->regex_arr.size())
10753 1035 break;
10754 else
10755 5 goto err;
10756 }
10757 /* we found the start */
10758 18874 reg.pattern = buf_p;
10759
10760 /* Find first argument -- pattern string to be removed */
10761
6/6
✓ Branch 0 taken 19354 times.
✓ Branch 1 taken 483288 times.
✓ Branch 2 taken 481 times.
✓ Branch 3 taken 18873 times.
✓ Branch 4 taken 502642 times.
✓ Branch 5 taken 1 times.
502643 PARSE_REGEX_ARG
10762
10763
5/6
✓ Branch 0 taken 18873 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18873 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 18873 times.
18874 if (p == expr_end || ++p == expr_end) goto err;
10764
10765 /* buf_p now points to the replacement pattern terminated with \0 */
10766 18873 reg.replace = buf_p;
10767
10768 /* Find second argument -- replace string to replace pattern */
10769
6/6
✓ Branch 0 taken 19538 times.
✓ Branch 1 taken 294407 times.
✓ Branch 2 taken 666 times.
✓ Branch 3 taken 18872 times.
✓ Branch 4 taken 313945 times.
✓ Branch 5 taken 1 times.
313946 PARSE_REGEX_ARG
10770
10771
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 18872 times.
18873 if (p == expr_end) goto err;
10772
10773 /* skip the ending '/' in the statement */
10774 18872 p++;
10775
10776 /* Check if we should do matching case insensitive */
10777
4/4
✓ Branch 0 taken 10279 times.
✓ Branch 1 taken 8593 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 10268 times.
18872 if (p < expr_end && *p == 'i') reg.icase = 1;
10778
10779 /* done parsing the statement, now place it in regex_arr */
10780
2/4
✓ Branch 0 taken 18872 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18872 times.
18872 if (res->regex_arr.push_back(reg)) die("Out of memory");
10781 }
10782 9628 res->odd_buf_len = res->even_buf_len = 8192;
10783
1/2
✓ Branch 0 taken 9628 times.
✗ Branch 1 not taken.
9628 res->even_buf = (char *)my_malloc(PSI_NOT_INSTRUMENTED, res->even_buf_len,
10784 MYF(MY_WME + MY_FAE));
10785
1/2
✓ Branch 0 taken 9628 times.
✗ Branch 1 not taken.
9628 res->odd_buf = (char *)my_malloc(PSI_NOT_INSTRUMENTED, res->odd_buf_len,
10786 MYF(MY_WME + MY_FAE));
10787 9628 res->buf = res->even_buf;
10788
10789 9628 return res;
10790
10791 7 err:
10792
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 my_free(res);
10793 7 die("Error parsing replace_regex \"%s\"", expr);
10794 return nullptr;
10795 }
10796
10797 /*
10798 Parse the regular expression to be used in all result files
10799 from now on.
10800
10801 The syntax is --replace_regex /from/to/i /from/to/i ...
10802 i means case-insensitive match. If omitted, the match is
10803 case-sensitive
10804
10805 */
10806 9822 void do_get_replace_regex(struct st_command *command) {
10807 9822 const char *expr = command->first_argument;
10808 9822 free_replace_regex();
10809 /* Allow variable for the *entire* list of replacements */
10810
2/2
✓ Branch 0 taken 963 times.
✓ Branch 1 taken 8859 times.
9822 if (*expr == '$') {
10811 963 VAR *val = var_get(expr, nullptr, false, true);
10812
1/2
✓ Branch 0 taken 963 times.
✗ Branch 1 not taken.
963 expr = val ? val->str_val : nullptr;
10813 }
10814
5/8
✓ Branch 0 taken 9822 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9635 times.
✓ Branch 3 taken 187 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 9628 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 9815 times.
9822 if (expr && *expr && !(glob_replace_regex = init_replace_regex(expr)))
10815 die("Could not init replace_regex");
10816 9815 command->last_argument = command->end;
10817 9815 }
10818
10819 12784176 void free_replace_regex() {
10820
2/2
✓ Branch 0 taken 9628 times.
✓ Branch 1 taken 12774548 times.
12784176 if (glob_replace_regex) {
10821 9628 my_free(glob_replace_regex->even_buf);
10822 9628 my_free(glob_replace_regex->odd_buf);
10823 9628 glob_replace_regex->~st_replace_regex();
10824 9628 my_free(glob_replace_regex);
10825 9628 glob_replace_regex = nullptr;
10826 }
10827 12784176 }
10828
10829 #ifndef WORD_BIT
10830 #define WORD_BIT (8 * sizeof(uint))
10831 #endif
10832
10833 #define SET_MALLOC_HUNC 64
10834 #define LAST_CHAR_CODE 259
10835
10836 struct REP_SET {
10837 uint *bits; /* Pointer to used sets */
10838 short next[LAST_CHAR_CODE]; /* Pointer to next sets */
10839 uint found_len; /* Best match to date */
10840 int found_offset;
10841 uint table_offset;
10842 uint size_of_bits; /* For convenience */
10843 };
10844
10845 struct REP_SETS {
10846 uint count; /* Number of sets */
10847 uint extra; /* Extra sets in buffer */
10848 uint invisible; /* Sets not chown */
10849 uint size_of_bits;
10850 REP_SET *set, *set_buffer;
10851 uint *bit_buffer;
10852 };
10853
10854 struct FOUND_SET {
10855 uint table_offset;
10856 int found_offset;
10857 };
10858
10859 struct FOLLOWS {
10860 int chr;
10861 uint table_offset;
10862 uint len;
10863 };
10864
10865 int init_sets(REP_SETS *sets, uint states);
10866 REP_SET *make_new_set(REP_SETS *sets);
10867 void make_sets_invisible(REP_SETS *sets);
10868 void free_last_set(REP_SETS *sets);
10869 void free_sets(REP_SETS *sets);
10870 void internal_set_bit(REP_SET *set, uint bit);
10871 void internal_clear_bit(REP_SET *set, uint bit);
10872 void or_bits(REP_SET *to, REP_SET *from);
10873 void copy_bits(REP_SET *to, REP_SET *from);
10874 int cmp_bits(REP_SET *set1, REP_SET *set2);
10875 int get_next_bit(REP_SET *set, uint lastpos);
10876 int find_set(REP_SETS *sets, REP_SET *find);
10877 int find_found(FOUND_SET *found_set, uint table_offset, int found_offset);
10878 uint start_at_word(const char *pos);
10879 uint end_of_word(const char *pos);
10880
10881 static uint found_sets = 0;
10882
10883 114777 static uint replace_len(const char *str) {
10884 114777 uint len = 0;
10885
2/2
✓ Branch 0 taken 2484099 times.
✓ Branch 1 taken 114777 times.
2598876 while (*str) {
10886 2484099 str++;
10887 2484099 len++;
10888 }
10889 114777 return len;
10890 }
10891
10892 /* Init a replace structure for further calls */
10893
10894 35286 REPLACE *init_replace(const char **from, const char **to, uint count,
10895 const char *word_end_chars) {
10896 static const int SPACE_CHAR = 256;
10897 static const int END_OF_LINE = 258;
10898
10899 uint i, j, states, set_nr, len, result_len, max_length, found_end, bits_set,
10900 bit_nr;
10901 int used_sets, chr, default_state;
10902 char used_chars[LAST_CHAR_CODE], is_word_end[256];
10903 const char *pos, **to_array;
10904 char *to_pos;
10905 REP_SETS sets;
10906 REP_SET *set, *start_states, *word_states, *new_set;
10907 FOLLOWS *follow, *follow_ptr;
10908 REPLACE *replace;
10909 FOUND_SET *found_set;
10910 REPLACE_STRING *rep_str;
10911
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 DBUG_TRACE;
10912
10913 /* Count number of states */
10914
2/2
✓ Branch 0 taken 56046 times.
✓ Branch 1 taken 35286 times.
91332 for (i = result_len = max_length = 0, states = 2; i < count; i++) {
10915 56046 len = replace_len(from[i]);
10916
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56046 times.
56046 if (!len) {
10917 errno = EINVAL;
10918 return nullptr;
10919 }
10920 56046 states += len + 1;
10921 56046 result_len += (uint)std::strlen(to[i]) + 1;
10922
2/2
✓ Branch 0 taken 52761 times.
✓ Branch 1 taken 3285 times.
56046 if (len > max_length) max_length = len;
10923 }
10924 35286 memset(is_word_end, 0, sizeof(is_word_end));
10925
2/2
✓ Branch 0 taken 211781 times.
✓ Branch 1 taken 35286 times.
247067 for (i = 0; word_end_chars[i]; i++) is_word_end[(uchar)word_end_chars[i]] = 1;
10926
10927
2/4
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35286 times.
35286 if (init_sets(&sets, states)) return nullptr;
10928 35286 found_sets = 0;
10929
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35286 times.
35286 if (!(found_set = (FOUND_SET *)my_malloc(
10930
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 PSI_NOT_INSTRUMENTED, sizeof(FOUND_SET) * max_length * count,
10931 MYF(MY_WME)))) {
10932 free_sets(&sets);
10933 return nullptr;
10934 }
10935
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 (void)make_new_set(&sets); /* Set starting set */
10936 35286 make_sets_invisible(&sets); /* Hide previous sets */
10937 35286 used_sets = -1;
10938
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 word_states = make_new_set(&sets); /* Start of new word */
10939
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 start_states = make_new_set(&sets); /* This is first state */
10940
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35286 times.
35286 if (!(follow = (FOLLOWS *)my_malloc(PSI_NOT_INSTRUMENTED,
10941
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 (states + 2) * sizeof(FOLLOWS),
10942 MYF(MY_WME)))) {
10943 free_sets(&sets);
10944 my_free(found_set);
10945 return nullptr;
10946 }
10947
10948 /* Init follow_ptr[] */
10949
2/2
✓ Branch 0 taken 56046 times.
✓ Branch 1 taken 35286 times.
91332 for (i = 0, states = 1, follow_ptr = follow + 1; i < count; i++) {
10950
3/4
✓ Branch 0 taken 115 times.
✓ Branch 1 taken 55931 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 115 times.
56046 if (from[i][0] == '\\' && from[i][1] == '^') {
10951 internal_set_bit(start_states, states + 1);
10952 if (!from[i][2]) {
10953 start_states->table_offset = i;
10954 start_states->found_offset = 1;
10955 }
10956
3/4
✓ Branch 0 taken 115 times.
✓ Branch 1 taken 55931 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 115 times.
56046 } else if (from[i][0] == '\\' && from[i][1] == '$') {
10957 internal_set_bit(start_states, states);
10958 internal_set_bit(word_states, states);
10959 if (!from[i][2] && start_states->table_offset == (uint)~0) {
10960 start_states->table_offset = i;
10961 start_states->found_offset = 0;
10962 }
10963 } else {
10964 56046 internal_set_bit(word_states, states);
10965
3/6
✓ Branch 0 taken 115 times.
✓ Branch 1 taken 55931 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 115 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
56046 if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
10966 internal_set_bit(start_states, states + 1);
10967 else
10968 56046 internal_set_bit(start_states, states);
10969 }
10970
2/2
✓ Branch 0 taken 1185675 times.
✓ Branch 1 taken 56046 times.
1241721 for (pos = from[i], len = 0; *pos; pos++) {
10971 1185675 follow_ptr->chr = (uchar)*pos;
10972 1185675 follow_ptr->table_offset = i;
10973 1185675 follow_ptr->len = ++len;
10974 1185675 follow_ptr++;
10975 }
10976 56046 follow_ptr->chr = 0;
10977 56046 follow_ptr->table_offset = i;
10978 56046 follow_ptr->len = len;
10979 56046 follow_ptr++;
10980 56046 states += (uint)len + 1;
10981 }
10982
10983
2/2
✓ Branch 0 taken 1155938 times.
✓ Branch 1 taken 35286 times.
1191224 for (set_nr = 0, pos = nullptr; set_nr < sets.count; set_nr++) {
10984 1155938 set = sets.set + set_nr;
10985 1155938 default_state = 0; /* Start from beginning */
10986
10987 /* If end of found-string not found or start-set with current set */
10988
10989
2/2
✓ Branch 0 taken 21897447 times.
✓ Branch 1 taken 1155938 times.
23053385 for (i = (uint)~0; (i = get_next_bit(set, i));) {
10990
2/2
✓ Branch 0 taken 3051 times.
✓ Branch 1 taken 21894396 times.
21897447 if (!follow[i].chr) {
10991
1/2
✓ Branch 0 taken 3051 times.
✗ Branch 1 not taken.
3051 if (!default_state)
10992 default_state =
10993 3051 find_found(found_set, set->table_offset, set->found_offset + 1);
10994 }
10995 }
10996 1155938 copy_bits(sets.set + used_sets, set); /* Save set for changes */
10997
2/2
✓ Branch 0 taken 1152887 times.
✓ Branch 1 taken 3051 times.
1155938 if (!default_state)
10998 1152887 or_bits(sets.set + used_sets, sets.set); /* Can restart from start */
10999
11000 /* Find all chars that follows current sets */
11001 1155938 memset(used_chars, 0, sizeof(used_chars));
11002
2/2
✓ Branch 0 taken 23676532 times.
✓ Branch 1 taken 1155938 times.
24832470 for (i = (uint)~0; (i = get_next_bit(sets.set + used_sets, i));) {
11003 23676532 used_chars[follow[i].chr] = 1;
11004
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 23676532 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
23676532 if ((follow[i].chr == SPACE_CHAR && !follow[i + 1].chr &&
11005 follow[i].len > 1) ||
11006
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23676532 times.
23676532 follow[i].chr == END_OF_LINE)
11007 used_chars[0] = 1;
11008 }
11009
11010 /* Mark word_chars used if \b is in state */
11011
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1155938 times.
1155938 if (used_chars[SPACE_CHAR])
11012 for (pos = word_end_chars; *pos; pos++) used_chars[(int)(uchar)*pos] = 1;
11013
11014 /* Handle other used characters */
11015
2/2
✓ Branch 0 taken 295920128 times.
✓ Branch 1 taken 1155938 times.
297076066 for (chr = 0; chr < 256; chr++) {
11016
2/2
✓ Branch 0 taken 293087791 times.
✓ Branch 1 taken 2832337 times.
295920128 if (!used_chars[chr])
11017
2/2
✓ Branch 0 taken 291934904 times.
✓ Branch 1 taken 1152887 times.
293087791 set->next[chr] = chr ? default_state : -1;
11018 else {
11019
1/2
✓ Branch 0 taken 2832337 times.
✗ Branch 1 not taken.
2832337 new_set = make_new_set(&sets);
11020
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2832337 times.
2832337 assert(new_set);
11021 2832337 set = sets.set + set_nr; /* if realloc */
11022 2832337 new_set->table_offset = set->table_offset;
11023 2832337 new_set->found_len = set->found_len;
11024 2832337 new_set->found_offset = set->found_offset + 1;
11025 2832337 found_end = 0;
11026
11027
2/2
✓ Branch 0 taken 30138475 times.
✓ Branch 1 taken 2832337 times.
32970812 for (i = (uint)~0; (i = get_next_bit(sets.set + used_sets, i));) {
11028
4/4
✓ Branch 0 taken 30132344 times.
✓ Branch 1 taken 6131 times.
✓ Branch 2 taken 6458863 times.
✓ Branch 3 taken 23673481 times.
30138475 if (!follow[i].chr || follow[i].chr == chr ||
11029
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6458863 times.
6458863 (follow[i].chr == SPACE_CHAR &&
11030 (is_word_end[chr] ||
11031 (!chr && follow[i].len > 1 && !follow[i + 1].chr))) ||
11032
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6458863 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
6458863 (follow[i].chr == END_OF_LINE && !chr)) {
11033
6/6
✓ Branch 0 taken 23676561 times.
✓ Branch 1 taken 3051 times.
✓ Branch 2 taken 23673481 times.
✓ Branch 3 taken 3080 times.
✓ Branch 4 taken 83819 times.
✓ Branch 5 taken 23589662 times.
23679612 if ((!chr || (follow[i].chr && !follow[i + 1].chr)) &&
11034
2/2
✓ Branch 0 taken 86867 times.
✓ Branch 1 taken 3 times.
86870 follow[i].len > found_end)
11035 86867 found_end = follow[i].len;
11036
4/4
✓ Branch 0 taken 23676561 times.
✓ Branch 1 taken 3051 times.
✓ Branch 2 taken 23673481 times.
✓ Branch 3 taken 3080 times.
23679612 if (chr && follow[i].chr)
11037 23673481 internal_set_bit(new_set, i + 1); /* To next set */
11038 else
11039 6131 internal_set_bit(new_set, i);
11040 }
11041 }
11042
2/2
✓ Branch 0 taken 86771 times.
✓ Branch 1 taken 2745566 times.
2832337 if (found_end) {
11043 86771 new_set->found_len = 0; /* Set for testing if first */
11044 86771 bits_set = 0;
11045
2/2
✓ Branch 0 taken 108151 times.
✓ Branch 1 taken 86771 times.
194922 for (i = (uint)~0; (i = get_next_bit(new_set, i));) {
11046
2/6
✓ Branch 0 taken 108151 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 108151 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
108151 if ((follow[i].chr == SPACE_CHAR || follow[i].chr == END_OF_LINE) &&
11047 !chr)
11048 bit_nr = i + 1;
11049 else
11050 108151 bit_nr = i;
11051
2/2
✓ Branch 0 taken 87011 times.
✓ Branch 1 taken 21140 times.
108151 if (follow[bit_nr - 1].len < found_end ||
11052
5/6
✓ Branch 0 taken 199 times.
✓ Branch 1 taken 86812 times.
✓ Branch 2 taken 199 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 196 times.
87011 (new_set->found_len && (chr == 0 || !follow[bit_nr].chr)))
11053 21143 internal_clear_bit(new_set, i);
11054 else {
11055
4/4
✓ Branch 0 taken 83957 times.
✓ Branch 1 taken 3051 times.
✓ Branch 2 taken 83720 times.
✓ Branch 3 taken 237 times.
87008 if (chr == 0 || !follow[bit_nr].chr) { /* best match */
11056 86771 new_set->table_offset = follow[bit_nr].table_offset;
11057
3/4
✓ Branch 0 taken 3051 times.
✓ Branch 1 taken 83720 times.
✓ Branch 2 taken 3051 times.
✗ Branch 3 not taken.
86771 if (chr || (follow[i].chr == SPACE_CHAR ||
11058
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3051 times.
3051 follow[i].chr == END_OF_LINE))
11059 83720 new_set->found_offset = found_end; /* New match */
11060 86771 new_set->found_len = found_end;
11061 }
11062 87008 bits_set++;
11063 }
11064 }
11065
2/2
✓ Branch 0 taken 86575 times.
✓ Branch 1 taken 196 times.
86771 if (bits_set == 1) {
11066 86575 set->next[chr] = find_found(found_set, new_set->table_offset,
11067 new_set->found_offset);
11068 86575 free_last_set(&sets);
11069 } else
11070 196 set->next[chr] = find_set(&sets, new_set);
11071 } else
11072 2745566 set->next[chr] = find_set(&sets, new_set);
11073 }
11074 }
11075 }
11076
11077 /* Alloc replace structure for the replace-state-machine */
11078
11079 35286 if ((replace =
11080
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 (REPLACE *)my_malloc(PSI_NOT_INSTRUMENTED,
11081 35286 sizeof(REPLACE) * (sets.count) +
11082 35286 sizeof(REPLACE_STRING) * (found_sets + 1) +
11083
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 sizeof(char *) * count + result_len,
11084 MYF(MY_WME | MY_ZEROFILL)))) {
11085 35286 rep_str = (REPLACE_STRING *)(replace + sets.count);
11086 35286 to_array = pointer_cast<const char **>(rep_str + found_sets + 1);
11087 35286 to_pos = (char *)(to_array + count);
11088
2/2
✓ Branch 0 taken 56046 times.
✓ Branch 1 taken 35286 times.
91332 for (i = 0; i < count; i++) {
11089 56046 to_array[i] = to_pos;
11090 56046 to_pos = my_stpcpy(to_pos, to[i]) + 1;
11091 }
11092 35286 rep_str[0].found = 1;
11093 35286 rep_str[0].replace_string = nullptr;
11094
2/2
✓ Branch 0 taken 58731 times.
✓ Branch 1 taken 35286 times.
94017 for (i = 1; i <= found_sets; i++) {
11095 58731 pos = from[found_set[i - 1].table_offset];
11096
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58731 times.
58731 rep_str[i].found = !memcmp(pos, "\\^", 3) ? 2 : 1;
11097 58731 rep_str[i].replace_string = to_array[found_set[i - 1].table_offset];
11098 58731 rep_str[i].to_offset = found_set[i - 1].found_offset - start_at_word(pos);
11099 58731 rep_str[i].from_offset =
11100 58731 found_set[i - 1].found_offset - replace_len(pos) + end_of_word(pos);
11101 }
11102
2/2
✓ Branch 0 taken 1155938 times.
✓ Branch 1 taken 35286 times.
1191224 for (i = 0; i < sets.count; i++) {
11103
2/2
✓ Branch 0 taken 295920128 times.
✓ Branch 1 taken 1155938 times.
297076066 for (j = 0; j < 256; j++)
11104
2/2
✓ Branch 0 taken 293905741 times.
✓ Branch 1 taken 2014387 times.
295920128 if (sets.set[i].next[j] >= 0)
11105 293905741 replace[i].next[j] = replace + sets.set[i].next[j];
11106 else
11107 2014387 replace[i].next[j] =
11108 2014387 (REPLACE *)(rep_str + (-sets.set[i].next[j] - 1));
11109 }
11110 }
11111
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 my_free(follow);
11112
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 free_sets(&sets);
11113
1/2
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
35286 my_free(found_set);
11114
3/8
✓ Branch 0 taken 35286 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35286 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 35286 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
35286 DBUG_PRINT("exit", ("Replace table has %d states", sets.count));
11115 35286 return replace;
11116 35286 }
11117
11118 35286 int init_sets(REP_SETS *sets, uint states) {
11119 35286 memset(sets, 0, sizeof(*sets));
11120 35286 sets->size_of_bits = ((states + 7) / 8);
11121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35286 times.
35286 if (!(sets->set_buffer = (REP_SET *)my_malloc(
11122 PSI_NOT_INSTRUMENTED, sizeof(REP_SET) * SET_MALLOC_HUNC,
11123 MYF(MY_WME))))
11124 return 1;
11125
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35286 times.
35286 if (!(sets->bit_buffer = (uint *)my_malloc(
11126 PSI_NOT_INSTRUMENTED,
11127 35286 sizeof(uint) * sets->size_of_bits * SET_MALLOC_HUNC,
11128 MYF(MY_WME)))) {
11129 my_free(sets->set);
11130 return 1;
11131 }
11132 35286 return 0;
11133 }
11134
11135 /* Make help sets invisible for nicer coding */
11136
11137 35286 void make_sets_invisible(REP_SETS *sets) {
11138 35286 sets->invisible = sets->count;
11139 35286 sets->set += sets->count;
11140 35286 sets->count = 0;
11141 35286 }
11142
11143 2977582 REP_SET *make_new_set(REP_SETS *sets) {
11144 uint i, count, *bit_buffer;
11145 REP_SET *set;
11146
2/2
✓ Branch 0 taken 2938195 times.
✓ Branch 1 taken 39387 times.
2977582 if (sets->extra) {
11147 2938195 sets->extra--;
11148 2938195 set = sets->set + sets->count++;
11149 2938195 memset(set->bits, 0, sizeof(uint) * sets->size_of_bits);
11150 2938195 memset(&set->next[0], 0, sizeof(set->next[0]) * LAST_CHAR_CODE);
11151 2938195 set->found_offset = 0;
11152 2938195 set->found_len = 0;
11153 2938195 set->table_offset = (uint)~0;
11154 2938195 set->size_of_bits = sets->size_of_bits;
11155 2938195 return set;
11156 }
11157 39387 count = sets->count + sets->invisible + SET_MALLOC_HUNC;
11158
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39387 times.
39387 if (!(set = (REP_SET *)my_realloc(PSI_NOT_INSTRUMENTED,
11159 39387 (uchar *)sets->set_buffer,
11160 39387 sizeof(REP_SET) * count, MYF(MY_WME))))
11161 return nullptr;
11162 39387 sets->set_buffer = set;
11163 39387 sets->set = set + sets->invisible;
11164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39387 times.
39387 if (!(bit_buffer = (uint *)my_realloc(
11165 39387 PSI_NOT_INSTRUMENTED, (uchar *)sets->bit_buffer,
11166 39387 (sizeof(uint) * sets->size_of_bits) * count, MYF(MY_WME))))
11167 return nullptr;
11168 39387 sets->bit_buffer = bit_buffer;
11169
2/2
✓ Branch 0 taken 3173376 times.
✓ Branch 1 taken 39387 times.
3212763 for (i = 0; i < count; i++) {
11170 3173376 sets->set_buffer[i].bits = bit_buffer;
11171 3173376 bit_buffer += sets->size_of_bits;
11172 }
11173 39387 sets->extra = SET_MALLOC_HUNC;
11174 39387 return make_new_set(sets);
11175 }
11176
11177 1746971 void free_last_set(REP_SETS *sets) {
11178 1746971 sets->count--;
11179 1746971 sets->extra++;
11180 1746971 return;
11181 }
11182
11183 35286 void free_sets(REP_SETS *sets) {
11184 35286 my_free(sets->set_buffer);
11185 35286 my_free(sets->bit_buffer);
11186 35286 return;
11187 }
11188
11189 23791704 void internal_set_bit(REP_SET *set, uint bit) {
11190 23791704 set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
11191 23791704 return;
11192 }
11193
11194 21143 void internal_clear_bit(REP_SET *set, uint bit) {
11195 21143 set->bits[bit / WORD_BIT] &= ~(1 << (bit % WORD_BIT));
11196 21143 return;
11197 }
11198
11199 1152887 void or_bits(REP_SET *to, REP_SET *from) {
11200 uint i;
11201
2/2
✓ Branch 0 taken 14824365 times.
✓ Branch 1 taken 1152887 times.
15977252 for (i = 0; i < to->size_of_bits; i++) to->bits[i] |= from->bits[i];
11202 1152887 return;
11203 }
11204
11205 1155938 void copy_bits(REP_SET *to, REP_SET *from) {
11206 1155938 memcpy((uchar *)to->bits, (uchar *)from->bits,
11207 1155938 (size_t)(sizeof(uint) * to->size_of_bits));
11208 1155938 }
11209
11210 58292358 int cmp_bits(REP_SET *set1, REP_SET *set2) {
11211 58292358 return memcmp(set1->bits, set2->bits, sizeof(uint) * set1->size_of_bits);
11212 }
11213
11214 /* Get next set bit from set. */
11215
11216 81051589 int get_next_bit(REP_SET *set, uint lastpos) {
11217 uint pos, *start, *end, bits;
11218
11219 81051589 start = set->bits + ((lastpos + 1) / WORD_BIT);
11220 81051589 end = set->bits + set->size_of_bits;
11221 81051589 bits = start[0] & ~((1 << ((lastpos + 1) % WORD_BIT)) - 1U);
11222
11223
6/6
✓ Branch 0 taken 59161313 times.
✓ Branch 1 taken 75820605 times.
✓ Branch 2 taken 53930329 times.
✓ Branch 3 taken 5230984 times.
✓ Branch 4 taken 53930329 times.
✓ Branch 5 taken 81051589 times.
134981918 while (!bits && ++start < end) bits = start[0];
11224
2/2
✓ Branch 0 taken 5230984 times.
✓ Branch 1 taken 75820605 times.
81051589 if (!bits) return 0;
11225 75820605 pos = (uint)(start - set->bits) * WORD_BIT;
11226
2/2
✓ Branch 0 taken 1085082082 times.
✓ Branch 1 taken 75820605 times.
1160902687 while (!(bits & 1)) {
11227 1085082082 bits >>= 1;
11228 1085082082 pos++;
11229 }
11230 75820605 return pos;
11231 }
11232
11233 /* find if there is a same set in sets. If there is, use it and
11234 free given set, else put in given set in sets and return its
11235 position */
11236
11237 2745762 int find_set(REP_SETS *sets, REP_SET *find) {
11238 uint i;
11239
2/2
✓ Branch 0 taken 58292358 times.
✓ Branch 1 taken 1085366 times.
59377724 for (i = 0; i < sets->count - 1; i++) {
11240
2/2
✓ Branch 0 taken 1660396 times.
✓ Branch 1 taken 56631962 times.
58292358 if (!cmp_bits(sets->set + i, find)) {
11241 1660396 free_last_set(sets);
11242 1660396 return i;
11243 }
11244 }
11245 1085366 return i; /* return new position */
11246 }
11247
11248 /* find if there is a found_set with same table_offset & found_offset
11249 If there is return offset to it, else add new offset and return pos.
11250 Pos returned is -offset-2 in found_set_structure because it is
11251 saved in set->next and set->next[] >= 0 points to next set and
11252 set->next[] == -1 is reserved for end without replaces.
11253 */
11254
11255 89626 int find_found(FOUND_SET *found_set, uint table_offset, int found_offset) {
11256 int i;
11257
2/2
✓ Branch 0 taken 121098 times.
✓ Branch 1 taken 58731 times.
179829 for (i = 0; (uint)i < found_sets; i++)
11258
2/2
✓ Branch 0 taken 86205 times.
✓ Branch 1 taken 34893 times.
121098 if (found_set[i].table_offset == table_offset &&
11259
2/2
✓ Branch 0 taken 30895 times.
✓ Branch 1 taken 55310 times.
86205 found_set[i].found_offset == found_offset)
11260 30895 return -i - 2;
11261 58731 found_set[i].table_offset = table_offset;
11262 58731 found_set[i].found_offset = found_offset;
11263 58731 found_sets++;
11264 58731 return -i - 2; /* return new position */
11265 }
11266
11267 /* Return 1 if regexp starts with \b or ends with \b*/
11268
11269 58731 uint start_at_word(const char *pos) {
11270
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 58731 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 58731 times.
58731 return (((!memcmp(pos, "\\b", 2) && pos[2]) || !memcmp(pos, "\\^", 2)) ? 1
11271 58731 : 0);
11272 }
11273
11274 58731 uint end_of_word(const char *pos) {
11275 58731 const char *end = strend(pos);
11276
1/2
✓ Branch 0 taken 57267 times.
✗ Branch 1 not taken.
57267 return ((end > pos + 2 && !memcmp(end - 2, "\\b", 2)) ||
11277
3/4
✓ Branch 0 taken 57963 times.
✓ Branch 1 taken 768 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 57963 times.
58731 (end >= pos + 2 && !memcmp(end - 2, "\\$", 2)))
11278
2/2
✓ Branch 0 taken 57267 times.
✓ Branch 1 taken 1464 times.
117462 ? 1
11279 58731 : 0;
11280 }
11281
11282 /****************************************************************************
11283 * Handle replacement of strings
11284 ****************************************************************************/
11285
11286 #define PC_MALLOC 256 /* Bytes for pointers */
11287 #define PS_MALLOC 512 /* Bytes for data */
11288
11289 112096 int insert_pointer_name(POINTER_ARRAY *pa, char *name) {
11290 uint i, length, old_count;
11291 uchar *new_pos;
11292 const char **new_array;
11293
1/2
✓ Branch 0 taken 112096 times.
✗ Branch 1 not taken.
112096 DBUG_TRACE;
11294
11295
2/2
✓ Branch 0 taken 70576 times.
✓ Branch 1 taken 41520 times.
112096 if (!pa->typelib.count) {
11296 70576 if (!(pa->typelib.type_names =
11297
2/4
✓ Branch 0 taken 70576 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 70576 times.
70576 (const char **)my_malloc(PSI_NOT_INSTRUMENTED,
11298 ((PC_MALLOC - MALLOC_OVERHEAD) /
11299 (sizeof(char *) + sizeof(*pa->flag)) *
11300 (sizeof(char *) + sizeof(*pa->flag))),
11301 MYF(MY_WME))))
11302 return -1;
11303
2/4
✓ Branch 0 taken 70576 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 70576 times.
70576 if (!(pa->str = (uchar *)my_malloc(PSI_NOT_INSTRUMENTED,
11304 (uint)(PS_MALLOC - MALLOC_OVERHEAD),
11305 MYF(MY_WME)))) {
11306 my_free(pa->typelib.type_names);
11307 return -1;
11308 }
11309 70576 pa->max_count =
11310 (PC_MALLOC - MALLOC_OVERHEAD) / (sizeof(uchar *) + sizeof(*pa->flag));
11311 70576 pa->flag = (uint8 *)(pa->typelib.type_names + pa->max_count);
11312 70576 pa->length = 0;
11313 70576 pa->max_length = PS_MALLOC - MALLOC_OVERHEAD;
11314 70576 pa->array_allocs = 1;
11315 }
11316 112096 length = (uint)std::strlen(name) + 1;
11317
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 112075 times.
112096 if (pa->length + length >= pa->max_length) {
11318
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if (!(new_pos = (uchar *)my_realloc(PSI_NOT_INSTRUMENTED, (uchar *)pa->str,
11319
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 (uint)(pa->length + length + PS_MALLOC),
11320 MYF(MY_WME))))
11321 return 1;
11322
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 if (new_pos != pa->str) {
11323 21 ptrdiff_t diff = new_pos - pa->str;
11324
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 21 times.
56 for (i = 0; i < pa->typelib.count; i++)
11325 35 pa->typelib.type_names[i] = pa->typelib.type_names[i] + diff;
11326 21 pa->str = new_pos;
11327 }
11328 21 pa->max_length = pa->length + length + PS_MALLOC;
11329 }
11330
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 112094 times.
112096 if (pa->typelib.count >= pa->max_count - 1) {
11331 int len;
11332 2 pa->array_allocs++;
11333 2 len = (PC_MALLOC * pa->array_allocs - MALLOC_OVERHEAD);
11334
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!(new_array = (const char **)my_realloc(
11335 2 PSI_NOT_INSTRUMENTED, (uchar *)pa->typelib.type_names,
11336
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 (uint)len / (sizeof(uchar *) + sizeof(*pa->flag)) *
11337 (sizeof(uchar *) + sizeof(*pa->flag)),
11338 MYF(MY_WME))))
11339 return 1;
11340 2 pa->typelib.type_names = new_array;
11341 2 old_count = pa->max_count;
11342 2 pa->max_count = len / (sizeof(uchar *) + sizeof(*pa->flag));
11343 2 pa->flag = (uint8 *)(pa->typelib.type_names + pa->max_count);
11344 2 memcpy((uchar *)pa->flag, (char *)(pa->typelib.type_names + old_count),
11345 old_count * sizeof(*pa->flag));
11346 }
11347 112096 pa->flag[pa->typelib.count] = 0; /* Reset flag */
11348 112096 pa->typelib.type_names[pa->typelib.count++] = (char *)pa->str + pa->length;
11349 112096 pa->typelib.type_names[pa->typelib.count] = NullS; /* Put end-mark */
11350 112096 (void)my_stpcpy((char *)pa->str + pa->length, name);
11351 112096 pa->length += length;
11352 112096 return 0;
11353 112096 } /* insert_pointer_name */
11354
11355 /* free pointer array */
11356
11357 70572 void free_pointer_array(POINTER_ARRAY *pa) {
11358
1/2
✓ Branch 0 taken 70572 times.
✗ Branch 1 not taken.
70572 if (pa->typelib.count) {
11359 70572 pa->typelib.count = 0;
11360 70572 my_free(pa->typelib.type_names);
11361 70572 pa->typelib.type_names = nullptr;
11362 70572 my_free(pa->str);
11363 }
11364 70572 } /* free_pointer_array */
11365
11366 /* Functions that uses replace and replace_regex */
11367
11368 /* Append the string to ds, with optional replace */
11369 379371246 void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
11370 size_t len) {
11371 #ifdef _WIN32
11372 fix_win_paths(val, len);
11373 #endif
11374
11375
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 379371189 times.
379371246 if (display_result_lower) {
11376 /* Convert to lower case, and do this first */
11377
1/2
✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
57 my_casedn_str(charset_info, const_cast<char *>(val));
11378 }
11379
11380
2/2
✓ Branch 0 taken 163041 times.
✓ Branch 1 taken 379208205 times.
379371246 if (glob_replace_regex) {
11381 163041 size_t orig_len = len;
11382 // Regex replace
11383
3/4
✓ Branch 0 taken 163033 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34311 times.
✓ Branch 3 taken 128722 times.
163041 if (!multi_reg_replace(glob_replace_regex, const_cast<char *>(val), &len)) {
11384 34311 val = glob_replace_regex->buf;
11385 } else {
11386 128722 len = orig_len;
11387 }
11388 }
11389
11390 DYNAMIC_STRING ds_temp;
11391
1/2
✓ Branch 0 taken 379371238 times.
✗ Branch 1 not taken.
379371238 init_dynamic_string(&ds_temp, "", 512);
11392
11393 /* Store result from replace_result in ds_temp */
11394
2/2
✓ Branch 0 taken 309590 times.
✓ Branch 1 taken 379061648 times.
379371238 if (glob_replace) {
11395 /* Normal replace */
11396
1/2
✓ Branch 0 taken 309590 times.
✗ Branch 1 not taken.
309590 replace_strings_append(glob_replace, &ds_temp, val, len);
11397 }
11398
11399 /*
11400 Call the replace_numeric_round function with the specified
11401 precision. It may be used along with replace_result, so use the
11402 output from replace_result as the input for replace_numeric_round.
11403 */
11404
2/2
✓ Branch 0 taken 3672 times.
✓ Branch 1 taken 379367566 times.
379371238 if (glob_replace_numeric_round >= 0) {
11405 /* Copy the result from replace_result if it was used, into buffer */
11406
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3672 times.
3672 if (ds_temp.length > 0) {
11407 char buffer[512];
11408 strcpy(buffer, ds_temp.str);
11409 dynstr_free(&ds_temp);
11410 init_dynamic_string(&ds_temp, "", 512);
11411 replace_numeric_round_append(glob_replace_numeric_round, &ds_temp, buffer,
11412 std::strlen(buffer));
11413 } else
11414
1/2
✓ Branch 0 taken 3672 times.
✗ Branch 1 not taken.
3672 replace_numeric_round_append(glob_replace_numeric_round, &ds_temp, val,
11415 len);
11416 }
11417
11418
4/4
✓ Branch 0 taken 379061648 times.
✓ Branch 1 taken 309590 times.
✓ Branch 2 taken 379057976 times.
✓ Branch 3 taken 3672 times.
379371238 if (!glob_replace && glob_replace_numeric_round < 0)
11419
1/2
✓ Branch 0 taken 379057976 times.
✗ Branch 1 not taken.
379057976 dynstr_append_mem(ds, val, len);
11420 else
11421
1/2
✓ Branch 0 taken 313262 times.
✗ Branch 1 not taken.
313262 dynstr_append_mem(ds, ds_temp.str, std::strlen(ds_temp.str));
11422
1/2
✓ Branch 0 taken 379371238 times.
✗ Branch 1 not taken.
379371238 dynstr_free(&ds_temp);
11423 379371238 }
11424
11425 /* Append zero-terminated string to ds, with optional replace */
11426 16404892 void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val) {
11427 16404892 replace_dynstr_append_mem(ds, val, std::strlen(val));
11428 16404892 }
11429
11430 /* Append uint to ds, with optional replace */
11431 22674 void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val) {
11432 char buff[22]; /* This should be enough for any int */
11433
1/2
✓ Branch 0 taken 22674 times.
✗ Branch 1 not taken.
22674 char *end = longlong10_to_str(val, buff, 10);
11434
1/2
✓ Branch 0 taken 22674 times.
✗ Branch 1 not taken.
22674 replace_dynstr_append_mem(ds, buff, end - buff);
11435 22674 }
11436
11437 /*
11438 Build a list of pointer to each line in ds_input, sort
11439 the list and use the sorted list to append the strings
11440 sorted to the output ds
11441
11442 SYNOPSIS
11443 dynstr_append_sorted
11444 ds - string where the sorted output will be appended
11445 ds_input - string to be sorted
11446 start_sort_column - column to start sorting from (0 for sorting
11447 the entire line); a stable sort will be used
11448 */
11449
11450 class Comp_lines {
11451 public:
11452 bool operator()(const char *a, const char *b) {
11453 return std::strcmp(a, b) < 0;
11454 }
11455 };
11456
11457 488 static size_t length_of_n_first_columns(std::string str,
11458 int start_sort_column) {
11459
1/2
✓ Branch 0 taken 488 times.
✗ Branch 1 not taken.
488 std::stringstream columns(str);
11460 488 std::string temp;
11461 488 size_t size_of_columns = 0;
11462
11463 488 int i = 0;
11464
8/10
✓ Branch 0 taken 1127 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1127 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1118 times.
✓ Branch 5 taken 9 times.
✓ Branch 6 taken 639 times.
✓ Branch 7 taken 479 times.
✓ Branch 8 taken 639 times.
✓ Branch 9 taken 488 times.
1127 while (getline(columns, temp, '\t') && i < start_sort_column) {
11465 639 size_of_columns = size_of_columns + temp.length();
11466 639 i++;
11467 }
11468
11469 488 return size_of_columns;
11470 488 }
11471
11472 31700 void dynstr_append_sorted(DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_input,
11473 int start_sort_column) {
11474 31700 char *start = ds_input->str;
11475 31700 char *end = ds_input->str + ds_input->length;
11476 31700 std::vector<std::string> sorted;
11477
1/2
✓ Branch 0 taken 31700 times.
✗ Branch 1 not taken.
31700 DBUG_TRACE;
11478
11479
2/2
✓ Branch 0 taken 235 times.
✓ Branch 1 taken 31465 times.
31700 if (!*start) return; /* No input */
11480
11481 /* First line is result header, skip past it */
11482
3/4
✓ Branch 0 taken 391937 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 360472 times.
✓ Branch 3 taken 31465 times.
391937 while (*start && *start != '\n') start++;
11483 31465 start++; /* Skip past \n */
11484
1/2
✓ Branch 0 taken 31465 times.
✗ Branch 1 not taken.
31465 dynstr_append_mem(ds, ds_input->str, start - ds_input->str);
11485
11486 /*
11487 Traverse through the result set from start to end to avoid
11488 ignoring null characters (0x00), and insert line by line
11489 into array.
11490 */
11491 31465 size_t first_unsorted_row = 0;
11492
2/2
✓ Branch 0 taken 243461 times.
✓ Branch 1 taken 31465 times.
274926 while (start < end) {
11493 243461 char *line_end = (char *)start;
11494
11495 /* Find end of line */
11496
2/2
✓ Branch 0 taken 6142145 times.
✓ Branch 1 taken 243461 times.
6385606 while (*line_end != '\n') line_end++;
11497 243461 *line_end = 0;
11498
11499
1/2
✓ Branch 0 taken 243461 times.
✗ Branch 1 not taken.
243461 std::string result_row = std::string(start, line_end - start);
11500
6/6
✓ Branch 0 taken 220736 times.
✓ Branch 1 taken 22725 times.
✓ Branch 2 taken 488 times.
✓ Branch 3 taken 220248 times.
✓ Branch 4 taken 488 times.
✓ Branch 5 taken 242973 times.
243461 if (!sorted.empty() && start_sort_column > 0) {
11501 /*
11502 If doing partial sorting, and the prefix is different from that of the
11503 previous line, the group is done. Sort it and start another one.
11504 */
11505 size_t prev_line_prefix_len =
11506
2/4
✓ Branch 0 taken 488 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 488 times.
✗ Branch 3 not taken.
488 length_of_n_first_columns(sorted.back(), start_sort_column);
11507
1/2
✓ Branch 0 taken 488 times.
✗ Branch 1 not taken.
488 if (sorted.back().compare(0, prev_line_prefix_len, result_row, 0,
11508
2/2
✓ Branch 0 taken 177 times.
✓ Branch 1 taken 311 times.
488 prev_line_prefix_len) != 0) {
11509
1/2
✓ Branch 0 taken 177 times.
✗ Branch 1 not taken.
177 std::sort(sorted.begin() + first_unsorted_row, sorted.end());
11510 177 first_unsorted_row = sorted.size();
11511 }
11512 }
11513
11514 /* Insert line into the array */
11515
1/2
✓ Branch 0 taken 243461 times.
✗ Branch 1 not taken.
243461 sorted.push_back(result_row);
11516 243461 start = line_end + 1;
11517 243461 }
11518
11519 /* Sort array */
11520
1/2
✓ Branch 0 taken 31465 times.
✗ Branch 1 not taken.
31465 std::stable_sort(sorted.begin() + first_unsorted_row, sorted.end());
11521
11522 /* Create new result */
11523
3/4
✓ Branch 0 taken 243461 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 243461 times.
✓ Branch 3 taken 31465 times.
274926 for (auto i : sorted) {
11524
1/2
✓ Branch 0 taken 243461 times.
✗ Branch 1 not taken.
243461 dynstr_append_mem(ds, i.c_str(), i.length());
11525
1/2
✓ Branch 0 taken 243461 times.
✗ Branch 1 not taken.
243461 dynstr_append(ds, "\n");
11526 243461 }
11527
4/4
✓ Branch 0 taken 31465 times.
✓ Branch 1 taken 235 times.
✓ Branch 2 taken 31465 times.
✓ Branch 3 taken 235 times.
31935 }
11528